diff --git a/mobile/lib/entities/album.entity.dart b/mobile/lib/entities/album.entity.dart index 8b466da1db..17e8e10a1a 100644 --- a/mobile/lib/entities/album.entity.dart +++ b/mobile/lib/entities/album.entity.dart @@ -163,10 +163,10 @@ class Album { dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc; } - if (dto.albumThumbnailAssetId != null) { + if (dto.albumThumbnailAssetId.unwrapOrNull() != null) { a.thumbnail.value = await db.assets .where() - .remoteIdEqualTo(dto.albumThumbnailAssetId) + .remoteIdEqualTo(dto.albumThumbnailAssetId.unwrap()) .findFirst(); } if (dto.albumUsers.isNotEmpty) { diff --git a/mobile/lib/entities/asset.entity.dart b/mobile/lib/entities/asset.entity.dart index 084cd1ee5d..b3b77f3c29 100644 --- a/mobile/lib/entities/asset.entity.dart +++ b/mobile/lib/entities/asset.entity.dart @@ -27,9 +27,9 @@ class Asset { durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, type = remote.type.toAssetType(), fileName = remote.originalFileName, - height = remote.exifInfo?.exifImageHeight?.toInt(), - width = remote.exifInfo?.exifImageWidth?.toInt(), - livePhotoVideoId = remote.livePhotoVideoId, + height = remote.exifInfo?.exifImageHeight.unwrapOrNull()?.toInt(), + width = remote.exifInfo?.exifImageWidth.unwrapOrNull()?.toInt(), + livePhotoVideoId = remote.livePhotoVideoId.unwrapOrNull(), ownerId = fastHash(remote.ownerId), exifInfo = remote.exifInfo == null ? null @@ -40,12 +40,13 @@ class Asset { isOffline = remote.isOffline, // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app // stack handling to properly handle it - stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id - ? null - : remote.stack?.primaryAssetId, - stackCount = remote.stack?.assetCount ?? 0, - stackId = remote.stack?.id, - thumbhash = remote.thumbhash; + stackPrimaryAssetId = + remote.stack.unwrapOrNull()?.primaryAssetId == remote.id + ? null + : remote.stack.unwrapOrNull()?.primaryAssetId, + stackCount = remote.stack.unwrapOrNull()?.assetCount ?? 0, + stackId = remote.stack.unwrapOrNull()?.id, + thumbhash = remote.thumbhash.unwrapOrNull(); Asset({ this.id = Isar.autoIncrement, diff --git a/mobile/lib/infrastructure/utils/exif.converter.dart b/mobile/lib/infrastructure/utils/exif.converter.dart index eb9945f454..204c44399a 100644 --- a/mobile/lib/infrastructure/utils/exif.converter.dart +++ b/mobile/lib/infrastructure/utils/exif.converter.dart @@ -5,24 +5,24 @@ import 'package:openapi/api.dart'; abstract final class ExifDtoConverter { static ExifInfo fromDto(ExifResponseDto dto) { return ExifInfo( - fileSize: dto.fileSizeInByte, - description: dto.description, - orientation: dto.orientation, - timeZone: dto.timeZone, - dateTimeOriginal: dto.dateTimeOriginal, - isFlipped: isOrientationFlipped(dto.orientation), - latitude: dto.latitude?.toDouble(), - longitude: dto.longitude?.toDouble(), - city: dto.city, - state: dto.state, - country: dto.country, - make: dto.make, - model: dto.model, - lens: dto.lensModel, - f: dto.fNumber?.toDouble(), - mm: dto.focalLength?.toDouble(), - iso: dto.iso?.toInt(), - exposureSeconds: _exposureTimeToSeconds(dto.exposureTime), + fileSize: dto.fileSizeInByte.unwrapOrNull(), + description: dto.description.unwrapOrNull(), + orientation: dto.orientation.unwrapOrNull(), + timeZone: dto.timeZone.unwrapOrNull(), + dateTimeOriginal: dto.dateTimeOriginal.unwrapOrNull(), + isFlipped: isOrientationFlipped(dto.orientation.unwrapOrNull()), + latitude: dto.latitude.unwrapOrNull()?.toDouble(), + longitude: dto.longitude.unwrapOrNull()?.toDouble(), + city: dto.city.unwrapOrNull(), + state: dto.state.unwrapOrNull(), + country: dto.country.unwrapOrNull(), + make: dto.make.unwrapOrNull(), + model: dto.model.unwrapOrNull(), + lens: dto.lensModel.unwrapOrNull(), + f: dto.fNumber.unwrapOrNull()?.toDouble(), + mm: dto.focalLength.unwrapOrNull()?.toDouble(), + iso: dto.iso.unwrapOrNull()?.toInt(), + exposureSeconds: _exposureTimeToSeconds(dto.exposureTime.unwrapOrNull()), ); } diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index eb7b24737e..fa51c34968 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -31,8 +31,8 @@ abstract final class UserConverter { inTimeline: false, isPartnerSharedBy: false, isPartnerSharedWith: false, - quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, - quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, + quotaUsageInBytes: adminDto.quotaUsageInBytes.unwrapOrNull() ?? 0, + quotaSizeInBytes: adminDto.quotaSizeInBytes.unwrapOrNull() ?? 0, ); static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( diff --git a/mobile/lib/models/shared_link/shared_link.model.dart b/mobile/lib/models/shared_link/shared_link.model.dart index a107dd892a..f8d95e4003 100644 --- a/mobile/lib/models/shared_link/shared_link.model.dart +++ b/mobile/lib/models/shared_link/shared_link.model.dart @@ -61,9 +61,9 @@ class SharedLink { : id = dto.id, allowDownload = dto.allowDownload, allowUpload = dto.allowUpload, - description = dto.description, - password = dto.password, - expiresAt = dto.expiresAt, + description = dto.description.unwrapOrNull(), + password = dto.password.unwrapOrNull(), + expiresAt = dto.expiresAt.unwrapOrNull(), key = dto.key, showMetadata = dto.showMetadata, type = dto.type == SharedLinkType.ALBUM @@ -73,7 +73,7 @@ class SharedLink { ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" : "INDIVIDUAL SHARE", thumbAssetId = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumThumbnailAssetId + ? dto.album?.albumThumbnailAssetId.unwrapOrNull() : dto.assets.isNotEmpty ? dto.assets[0].id : null; diff --git a/mobile/lib/providers/search/search_page_state.provider.dart b/mobile/lib/providers/search/search_page_state.provider.dart index d0e3720c0f..bd472e2865 100644 --- a/mobile/lib/providers/search/search_page_state.provider.dart +++ b/mobile/lib/providers/search/search_page_state.provider.dart @@ -1,6 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/search/search_curated_content.model.dart'; - import 'package:immich_mobile/services/search.service.dart'; final getPreviewPlacesProvider = @@ -41,7 +40,7 @@ final getAllPlacesProvider = final curatedContent = assetPlaces .map( (data) => SearchCuratedContent( - label: data.exifInfo!.city!, + label: data.exifInfo!.city.unwrap(), id: data.id, ), ) diff --git a/mobile/lib/repositories/activity_api.repository.dart b/mobile/lib/repositories/activity_api.repository.dart index 868415caf9..8896165718 100644 --- a/mobile/lib/repositories/activity_api.repository.dart +++ b/mobile/lib/repositories/activity_api.repository.dart @@ -61,7 +61,7 @@ class ActivityApiRepository extends ApiRepository ? ActivityType.comment : ActivityType.like, user: UserConverter.fromSimpleUserDto(dto.user), - assetId: dto.assetId, - comment: dto.comment, + assetId: dto.assetId.unwrapOrNull(), + comment: dto.comment.unwrapOrNull(), ); } diff --git a/mobile/lib/repositories/album_api.repository.dart b/mobile/lib/repositories/album_api.repository.dart index a7bbe452e6..fa31e44695 100644 --- a/mobile/lib/repositories/album_api.repository.dart +++ b/mobile/lib/repositories/album_api.repository.dart @@ -168,7 +168,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository { album.remoteAssetCount = dto.assetCount; album.owner.value = entity.User.fromDto(UserConverter.fromSimpleUserDto(dto.owner)); - album.remoteThumbnailAssetId = dto.albumThumbnailAssetId; + album.remoteThumbnailAssetId = dto.albumThumbnailAssetId.unwrapOrNull(); final users = dto.albumUsers .map((albumUser) => UserConverter.fromSimpleUserDto(albumUser.user)); album.sharedUsers.addAll(users.map(entity.User.fromDto)); diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index f4fcd8a6dd..2c138ccdc7 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -43,7 +43,7 @@ class AssetApiRepository extends ApiRepository implements IAssetApiRepository { ), ); result.addAll(response.assets.items.map(Asset.remote)); - hasNext = response.assets.nextPage != null; + hasNext = response.assets.nextPage.unwrapOrNull() != null; currentPage++; } return result; diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index d324a03edb..a8ed6d7da7 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -29,7 +29,7 @@ class PersonApiRepository extends ApiRepository } static Person _toPerson(PersonResponseDto dto) => Person( - birthDate: dto.birthDate, + birthDate: dto.birthDate.unwrapOrNull(), id: dto.id, isHidden: dto.isHidden, name: dto.name, diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart index bcf67889c0..4575702cc5 100644 --- a/mobile/lib/services/search.service.dart +++ b/mobile/lib/services/search.service.dart @@ -1,13 +1,14 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/models/search/search_result.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/option.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -61,11 +62,11 @@ class SearchService { SmartSearchDto( query: filter.context!, language: filter.language, - country: filter.location.country, - state: filter.location.state, - city: filter.location.city, + country: Option.from(filter.location.country), + state: Option.from(filter.location.state), + city: Option.from(filter.location.city), make: filter.camera.make, - model: filter.camera.model, + model: Option.from(filter.camera.model), takenAfter: filter.date.takenAfter, takenBefore: filter.date.takenBefore, visibility: filter.display.isArchive @@ -86,15 +87,15 @@ class SearchService { filter.filename != null && filter.filename!.isNotEmpty ? filter.filename : null, - country: filter.location.country, + country: Option.from(filter.location.country), description: filter.description != null && filter.description!.isNotEmpty ? filter.description : null, - state: filter.location.state, - city: filter.location.city, + state: Option.from(filter.location.state), + city: Option.from(filter.location.city), make: filter.camera.make, - model: filter.camera.model, + model: Option.from(filter.camera.model), takenAfter: filter.date.takenAfter, takenBefore: filter.date.takenBefore, visibility: filter.display.isArchive @@ -118,7 +119,7 @@ class SearchService { assets: await _assetRepository.getAllByRemoteId( response.assets.items.map((e) => e.id), ), - nextPage: response.assets.nextPage?.toInt(), + nextPage: response.assets.nextPage.unwrapOrNull()?.toInt(), ); } catch (error, stackTrace) { _log.severe("Failed to search for assets", error, stackTrace); diff --git a/mobile/lib/services/shared_link.service.dart b/mobile/lib/services/shared_link.service.dart index a2b5ed9062..83f0a5659b 100644 --- a/mobile/lib/services/shared_link.service.dart +++ b/mobile/lib/services/shared_link.service.dart @@ -2,6 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/shared_link/shared_link.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/option.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -56,7 +57,7 @@ class SharedLinkService { showMetadata: showMeta, allowDownload: allowDownload, allowUpload: allowUpload, - expiresAt: expiresAt, + expiresAt: Option.from(expiresAt), description: description, password: password, ); @@ -66,7 +67,7 @@ class SharedLinkService { showMetadata: showMeta, allowDownload: allowDownload, allowUpload: allowUpload, - expiresAt: expiresAt, + expiresAt: Option.from(expiresAt), description: description, password: password, assetIds: assetIds, @@ -103,7 +104,7 @@ class SharedLinkService { showMetadata: showMeta, allowDownload: allowDownload, allowUpload: allowUpload, - expiresAt: expiresAt, + expiresAt: Option.from(expiresAt), description: description, password: password, changeExpiryTime: changeExpiry, diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart index 8d2a38c700..793d63053b 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart @@ -45,9 +45,9 @@ class PeopleInfo extends ConsumerWidget { (p) => SearchCuratedContent( id: p.id, label: p.name, - subtitle: p.birthDate != null && - p.birthDate!.isBefore(asset.fileCreatedAt) - ? _formatAge(p.birthDate!, asset.fileCreatedAt) + subtitle: p.birthDate.unwrapOrNull() != null && + p.birthDate.unwrap().isBefore(asset.fileCreatedAt) + ? _formatAge(p.birthDate.unwrap(), asset.fileCreatedAt) : null, ), ) diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index ba97f1434a..e87213893a 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -1,11 +1,12 @@ import 'package:immich_mobile/domain/models/sync_event.model.dart'; +import 'package:immich_mobile/utils/option.dart'; import 'package:openapi/api.dart'; abstract final class SyncStreamStub { static final userV1Admin = SyncEvent( type: SyncEntityType.userV1, data: SyncUserV1( - deletedAt: DateTime(2020), + deletedAt: Some(DateTime(2020)), email: "admin@admin", id: "1", name: "Admin", @@ -15,7 +16,7 @@ abstract final class SyncStreamStub { static final userV1User = SyncEvent( type: SyncEntityType.userV1, data: SyncUserV1( - deletedAt: DateTime(2021), + deletedAt: Some(DateTime(2021)), email: "user@user", id: "5", name: "User",