From d39e64040f1e9b2c29d1443bfe35a5c3c7ffe521 Mon Sep 17 00:00:00 2001 From: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Date: Thu, 6 Mar 2025 23:14:26 +0530 Subject: [PATCH] refactor: user entity --- mobile/analysis_options.yaml | 3 +- .../lib/domain/interfaces/user.interface.dart | 24 ++ mobile/lib/domain/models/store.model.dart | 2 +- mobile/lib/domain/models/user.model.dart | 156 +++++++++ mobile/lib/domain/services/store.service.dart | 4 +- mobile/lib/entities/album.entity.dart | 2 +- mobile/lib/entities/user.entity.dart | 181 ---------- .../lib/extensions/collection_extensions.dart | 4 +- .../infrastructure/entities/user.entity.dart | 74 +++++ .../entities/user.entity.g.dart | 309 ++++-------------- .../repositories/store.repository.dart | 8 +- .../repositories/user.repository.dart | 80 +++++ .../infrastructure/utils/user.converter.dart | 66 ++++ mobile/lib/interfaces/album.interface.dart | 2 +- mobile/lib/interfaces/partner.interface.dart | 2 +- .../lib/interfaces/partner_api.interface.dart | 2 +- mobile/lib/interfaces/user.interface.dart | 27 -- mobile/lib/interfaces/user_api.interface.dart | 2 +- .../lib/models/activities/activity.model.dart | 2 +- ...additional_shared_user_selection.page.dart | 6 +- .../lib/pages/album/album_options.page.dart | 24 +- .../pages/album/album_shared_user_icons.dart | 4 +- .../album_shared_user_selection.page.dart | 4 +- mobile/lib/pages/albums/albums.page.dart | 2 +- mobile/lib/pages/common/activities.page.dart | 8 +- mobile/lib/pages/library/library.page.dart | 2 +- .../pages/library/partner/partner.page.dart | 2 +- .../library/partner/partner_detail.page.dart | 6 +- mobile/lib/pages/photos/photos.page.dart | 8 +- .../lib/providers/album/album.provider.dart | 6 +- .../suggested_shared_users.provider.dart | 10 +- mobile/lib/providers/auth.provider.dart | 15 +- .../infrastructure/store.provider.dart | 9 +- .../infrastructure/store.provider.g.dart | 24 +- .../infrastructure/user.provider.dart | 10 + .../infrastructure/user.provider.g.dart | 25 ++ mobile/lib/providers/partner.provider.dart | 2 +- mobile/lib/providers/user.provider.dart | 5 +- .../repositories/activity_api.repository.dart | 4 +- mobile/lib/repositories/album.repository.dart | 19 +- .../repositories/album_api.repository.dart | 11 +- .../repositories/album_media.repository.dart | 3 +- .../repositories/asset_media.repository.dart | 2 +- mobile/lib/repositories/auth.repository.dart | 2 +- .../lib/repositories/partner.repository.dart | 46 +-- .../repositories/partner_api.repository.dart | 9 +- .../lib/repositories/timeline.repository.dart | 2 +- mobile/lib/repositories/user.repository.dart | 73 ----- .../lib/repositories/user_api.repository.dart | 5 +- mobile/lib/routing/router.dart | 4 +- .../lib/routing/tab_navigation_observer.dart | 4 +- mobile/lib/services/album.service.dart | 17 +- mobile/lib/services/asset.service.dart | 34 +- mobile/lib/services/background.service.dart | 7 +- .../services/backup_verification.service.dart | 2 +- mobile/lib/services/entity.service.dart | 12 +- mobile/lib/services/partner.service.dart | 28 +- mobile/lib/services/sync.service.dart | 52 +-- mobile/lib/services/timeline.service.dart | 35 +- mobile/lib/services/trash.service.dart | 25 +- mobile/lib/services/user.service.dart | 48 +-- mobile/lib/utils/bootstrap.dart | 2 +- mobile/lib/utils/migration.dart | 2 +- .../widgets/album/album_thumbnail_card.dart | 2 +- .../asset_viewer/bottom_gallery_bar.dart | 20 +- .../asset_viewer/description_input.dart | 2 +- .../app_bar_dialog/app_bar_profile_info.dart | 5 +- mobile/lib/widgets/common/user_avatar.dart | 2 +- .../widgets/common/user_circle_avatar.dart | 6 +- mobile/test/fixtures/album.stub.dart | 5 +- mobile/test/fixtures/user.stub.dart | 28 +- .../repositories/store_repository_test.dart | 2 +- .../activity/activities_page_test.dart | 4 +- mobile/test/modules/shared/shared_mocks.dart | 2 +- .../modules/shared/sync_service_test.dart | 25 +- mobile/test/repository.mocks.dart | 2 +- mobile/test/services/album.service_test.dart | 6 +- mobile/test/services/entity.service_test.dart | 21 +- mobile/test/test_utils.dart | 2 +- 79 files changed, 881 insertions(+), 823 deletions(-) create mode 100644 mobile/lib/domain/interfaces/user.interface.dart create mode 100644 mobile/lib/domain/models/user.model.dart delete mode 100644 mobile/lib/entities/user.entity.dart create mode 100644 mobile/lib/infrastructure/entities/user.entity.dart rename mobile/lib/{ => infrastructure}/entities/user.entity.g.dart (86%) create mode 100644 mobile/lib/infrastructure/repositories/user.repository.dart create mode 100644 mobile/lib/infrastructure/utils/user.converter.dart delete mode 100644 mobile/lib/interfaces/user.interface.dart create mode 100644 mobile/lib/providers/infrastructure/user.provider.dart create mode 100644 mobile/lib/providers/infrastructure/user.provider.g.dart delete mode 100644 mobile/lib/repositories/user.repository.dart diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 4e20acb72a..085449756d 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -67,7 +67,7 @@ custom_lint: - lib/entities/*.entity.dart - lib/repositories/{album,asset,backup,database,etag,exif_info,user,timeline,partner}.repository.dart - lib/infrastructure/entities/*.entity.dart - - lib/infrastructure/repositories/{store,db,log,exif}.repository.dart + - lib/infrastructure/repositories/*.repository.dart - lib/providers/infrastructure/db.provider.dart # acceptable exceptions for the time being (until Isar is fully replaced) - lib/providers/app_life_cycle.provider.dart @@ -93,6 +93,7 @@ custom_lint: - lib/infrastructure/utils/*.converter.dart # acceptable exceptions for the time being - lib/entities/{album,asset,exif_info,user}.entity.dart # to convert DTOs to entities + - lib/infrastructure/utils/*.converter.dart - lib/utils/{image_url_builder,openapi_patching}.dart # utils are fine - test/modules/utils/openapi_patching_test.dart # filename is self-explanatory... - lib/domain/services/sync_stream.service.dart # Making sure to comply with the type from database diff --git a/mobile/lib/domain/interfaces/user.interface.dart b/mobile/lib/domain/interfaces/user.interface.dart new file mode 100644 index 0000000000..d92eb3c965 --- /dev/null +++ b/mobile/lib/domain/interfaces/user.interface.dart @@ -0,0 +1,24 @@ +import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; + +abstract interface class IUserRepository implements IDatabaseRepository { + Future insert(User user); + + Future get(int id); + + Future getByUserId(String id); + + Future> getByUserIds(List ids); + + Future> getAll({SortUserBy? sortBy}); + + Future updateAll(List users); + + Future update(User user); + + Future delete(List ids); + + Future deleteAll(); +} + +enum SortUserBy { id } diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 06b946b3f6..8eff710948 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -1,4 +1,4 @@ -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; /// Key for each possible value in the `Store`. /// Defines the data type for each value diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart new file mode 100644 index 0000000000..3ef5a78d0d --- /dev/null +++ b/mobile/lib/domain/models/user.model.dart @@ -0,0 +1,156 @@ +import 'dart:ui'; + +import 'package:immich_mobile/utils/hash.dart'; + +enum AvatarColor { + // do not change this order or reuse indices for other purposes, adding is OK + primary, + pink, + red, + yellow, + blue, + green, + purple, + orange, + gray, + amber; + + Color toColor({bool isDarkTheme = false}) => switch (this) { + AvatarColor.primary => + isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), + AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), + AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), + AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), + AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), + AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), + AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), + AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), + AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), + AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), + }; +} + +class User { + final String uid; + final String email; + final String name; + final bool isAdmin; + final DateTime updatedAt; + + final String? profileImagePath; + final AvatarColor avatarColor; + + final bool memoryEnabled; + final bool inTimeline; + + final bool isPartnerSharedBy; + final bool isPartnerSharedWith; + + final int quotaUsageInBytes; + final int quotaSizeInBytes; + + int get id => fastHash(uid); + bool get hasQuota => quotaSizeInBytes > 0; + + const User({ + required this.uid, + required this.email, + required this.name, + required this.isAdmin, + required this.updatedAt, + this.profileImagePath, + this.avatarColor = AvatarColor.primary, + this.memoryEnabled = true, + this.inTimeline = false, + this.isPartnerSharedBy = false, + this.isPartnerSharedWith = false, + this.quotaUsageInBytes = 0, + this.quotaSizeInBytes = 0, + }); + + @override + String toString() { + return '''User: { +id: $id, +uid: $uid, +email: $email, +name: $name, +isAdmin: $isAdmin, +updatedAt: $updatedAt, +profileImagePath: ${profileImagePath ?? ''}, +avatarColor: $avatarColor, +memoryEnabled: $memoryEnabled, +inTimeline: $inTimeline, +isPartnerSharedBy: $isPartnerSharedBy, +isPartnerSharedWith: $isPartnerSharedWith, +quotaUsageInBytes: $quotaUsageInBytes, +quotaSizeInBytes: $quotaSizeInBytes, +}'''; + } + + User copyWith({ + String? uid, + String? email, + String? name, + bool? isAdmin, + DateTime? updatedAt, + String? profileImagePath, + AvatarColor? avatarColor, + bool? memoryEnabled, + bool? inTimeline, + bool? isPartnerSharedBy, + bool? isPartnerSharedWith, + int? quotaUsageInBytes, + int? quotaSizeInBytes, + }) => + User( + uid: uid ?? this.uid, + email: email ?? this.email, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + updatedAt: updatedAt ?? this.updatedAt, + profileImagePath: profileImagePath ?? this.profileImagePath, + avatarColor: avatarColor ?? this.avatarColor, + memoryEnabled: memoryEnabled ?? this.memoryEnabled, + inTimeline: inTimeline ?? this.inTimeline, + isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + ); + + @override + bool operator ==(covariant User other) { + if (identical(this, other)) return true; + + return other.uid == uid && + other.updatedAt.isAtSameMomentAs(updatedAt) && + other.avatarColor == avatarColor && + other.email == email && + other.name == name && + other.isPartnerSharedBy == isPartnerSharedBy && + other.isPartnerSharedWith == isPartnerSharedWith && + other.profileImagePath == profileImagePath && + other.isAdmin == isAdmin && + other.memoryEnabled == memoryEnabled && + other.inTimeline == inTimeline && + other.quotaUsageInBytes == quotaUsageInBytes && + other.quotaSizeInBytes == quotaSizeInBytes; + } + + @override + int get hashCode => + uid.hashCode ^ + name.hashCode ^ + email.hashCode ^ + updatedAt.hashCode ^ + isAdmin.hashCode ^ + profileImagePath.hashCode ^ + avatarColor.hashCode ^ + memoryEnabled.hashCode ^ + inTimeline.hashCode ^ + isPartnerSharedBy.hashCode ^ + isPartnerSharedWith.hashCode ^ + quotaUsageInBytes.hashCode ^ + quotaSizeInBytes.hashCode; +} diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index 70b9f31c00..73426cbf4e 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -74,7 +74,7 @@ class StoreService { return value; } - /// Asynchronously stores the value in the DB and synchronously in the cache + /// Asynchronously stores the value in the Store Future put, T>(U key, T value) async { if (_cache[key.id] == value) return; await _storeRepository.insert(key, value); @@ -84,7 +84,7 @@ class StoreService { /// Watches a specific key for changes Stream watch(StoreKey key) => _storeRepository.watch(key); - /// Removes the value asynchronously from the DB and synchronously from the cache + /// Removes the value asynchronously from the Store Future delete(StoreKey key) async { await _storeRepository.delete(key); _cache.remove(key.id); diff --git a/mobile/lib/entities/album.entity.dart b/mobile/lib/entities/album.entity.dart index 8caff2255f..8b466da1db 100644 --- a/mobile/lib/entities/album.entity.dart +++ b/mobile/lib/entities/album.entity.dart @@ -1,7 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/utils/datetime_comparison.dart'; import 'package:isar/isar.dart'; // ignore: implementation_imports diff --git a/mobile/lib/entities/user.entity.dart b/mobile/lib/entities/user.entity.dart deleted file mode 100644 index 8fa6e83874..0000000000 --- a/mobile/lib/entities/user.entity.dart +++ /dev/null @@ -1,181 +0,0 @@ -import 'dart:ui'; - -import 'package:immich_mobile/entities/album.entity.dart'; -import 'package:immich_mobile/utils/hash.dart'; -import 'package:isar/isar.dart'; -import 'package:openapi/api.dart'; - -part 'user.entity.g.dart'; - -@Collection(inheritance: false) -class User { - User({ - required this.id, - required this.updatedAt, - required this.email, - required this.name, - required this.isAdmin, - this.isPartnerSharedBy = false, - this.isPartnerSharedWith = false, - this.profileImagePath = '', - this.avatarColor = AvatarColorEnum.primary, - this.memoryEnabled = true, - this.inTimeline = false, - this.quotaUsageInBytes = 0, - this.quotaSizeInBytes = 0, - }); - - Id get isarId => fastHash(id); - - User.fromUserDto( - UserAdminResponseDto dto, - UserPreferencesResponseDto? preferences, - ) : id = dto.id, - updatedAt = dto.updatedAt, - email = dto.email, - name = dto.name, - isPartnerSharedBy = false, - isPartnerSharedWith = false, - profileImagePath = dto.profileImagePath, - isAdmin = dto.isAdmin, - memoryEnabled = preferences?.memories.enabled ?? false, - avatarColor = dto.avatarColor.toAvatarColor(), - inTimeline = false, - quotaUsageInBytes = dto.quotaUsageInBytes ?? 0, - quotaSizeInBytes = dto.quotaSizeInBytes ?? 0; - - User.fromPartnerDto(PartnerResponseDto dto) - : id = dto.id, - updatedAt = DateTime.now(), - email = dto.email, - name = dto.name, - isPartnerSharedBy = false, - isPartnerSharedWith = false, - profileImagePath = dto.profileImagePath, - isAdmin = false, - memoryEnabled = false, - avatarColor = dto.avatarColor.toAvatarColor(), - inTimeline = dto.inTimeline ?? false, - quotaUsageInBytes = 0, - quotaSizeInBytes = 0; - - /// Base user dto used where the complete user object is not required - User.fromSimpleUserDto(UserResponseDto dto) - : id = dto.id, - email = dto.email, - name = dto.name, - profileImagePath = dto.profileImagePath, - avatarColor = dto.avatarColor.toAvatarColor(), - // Fill the remaining fields with placeholders - isAdmin = false, - inTimeline = false, - memoryEnabled = false, - isPartnerSharedBy = false, - isPartnerSharedWith = false, - updatedAt = DateTime.now(), - quotaUsageInBytes = 0, - quotaSizeInBytes = 0; - - @Index(unique: true, replace: false, type: IndexType.hash) - String id; - DateTime updatedAt; - String email; - String name; - bool isPartnerSharedBy; - bool isPartnerSharedWith; - bool isAdmin; - String profileImagePath; - @Enumerated(EnumType.ordinal) - AvatarColorEnum avatarColor; - bool memoryEnabled; - bool inTimeline; - int quotaUsageInBytes; - int quotaSizeInBytes; - - bool get hasQuota => quotaSizeInBytes > 0; - @Backlink(to: 'owner') - final IsarLinks albums = IsarLinks(); - @Backlink(to: 'sharedUsers') - final IsarLinks sharedAlbums = IsarLinks(); - - @override - bool operator ==(other) { - if (other is! User) return false; - return id == other.id && - updatedAt.isAtSameMomentAs(other.updatedAt) && - avatarColor == other.avatarColor && - email == other.email && - name == other.name && - isPartnerSharedBy == other.isPartnerSharedBy && - isPartnerSharedWith == other.isPartnerSharedWith && - profileImagePath == other.profileImagePath && - isAdmin == other.isAdmin && - memoryEnabled == other.memoryEnabled && - inTimeline == other.inTimeline && - quotaUsageInBytes == other.quotaUsageInBytes && - quotaSizeInBytes == other.quotaSizeInBytes; - } - - @override - @ignore - int get hashCode => - id.hashCode ^ - updatedAt.hashCode ^ - email.hashCode ^ - name.hashCode ^ - isPartnerSharedBy.hashCode ^ - isPartnerSharedWith.hashCode ^ - profileImagePath.hashCode ^ - avatarColor.hashCode ^ - isAdmin.hashCode ^ - memoryEnabled.hashCode ^ - inTimeline.hashCode ^ - quotaUsageInBytes.hashCode ^ - quotaSizeInBytes.hashCode; -} - -enum AvatarColorEnum { - // do not change this order or reuse indices for other purposes, adding is OK - primary, - pink, - red, - yellow, - blue, - green, - purple, - orange, - gray, - amber, -} - -extension AvatarColorEnumHelper on UserAvatarColor { - AvatarColorEnum toAvatarColor() => switch (this) { - UserAvatarColor.primary => AvatarColorEnum.primary, - UserAvatarColor.pink => AvatarColorEnum.pink, - UserAvatarColor.red => AvatarColorEnum.red, - UserAvatarColor.yellow => AvatarColorEnum.yellow, - UserAvatarColor.blue => AvatarColorEnum.blue, - UserAvatarColor.green => AvatarColorEnum.green, - UserAvatarColor.purple => AvatarColorEnum.purple, - UserAvatarColor.orange => AvatarColorEnum.orange, - UserAvatarColor.gray => AvatarColorEnum.gray, - UserAvatarColor.amber => AvatarColorEnum.amber, - _ => AvatarColorEnum.primary, - }; -} - -extension AvatarColorToColorHelper on AvatarColorEnum { - Color toColor([bool isDarkTheme = false]) => switch (this) { - AvatarColorEnum.primary => - isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), - AvatarColorEnum.pink => const Color.fromARGB(255, 244, 114, 182), - AvatarColorEnum.red => const Color.fromARGB(255, 239, 68, 68), - AvatarColorEnum.yellow => const Color.fromARGB(255, 234, 179, 8), - AvatarColorEnum.blue => const Color.fromARGB(255, 59, 130, 246), - AvatarColorEnum.green => const Color.fromARGB(255, 22, 163, 74), - AvatarColorEnum.purple => const Color.fromARGB(255, 147, 51, 234), - AvatarColorEnum.orange => const Color.fromARGB(255, 234, 88, 12), - AvatarColorEnum.gray => const Color.fromARGB(255, 75, 85, 99), - AvatarColorEnum.amber => const Color.fromARGB(255, 217, 119, 6), - }; -} diff --git a/mobile/lib/extensions/collection_extensions.dart b/mobile/lib/extensions/collection_extensions.dart index d27c9e9500..1e06bb93e2 100644 --- a/mobile/lib/extensions/collection_extensions.dart +++ b/mobile/lib/extensions/collection_extensions.dart @@ -1,8 +1,8 @@ import 'dart:typed_data'; import 'package:collection/collection.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; extension ListExtension on List { List uniqueConsecutive({ @@ -62,7 +62,7 @@ extension AssetListExtension on Iterable { void Function()? errorCallback, }) { if (owner == null) return []; - final userId = owner.isarId; + final userId = owner.id; final bool onlyOwned = every((e) => e.ownerId == userId); if (!onlyOwned) { if (errorCallback != null) errorCallback(); diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart new file mode 100644 index 0000000000..47ce7d4bd1 --- /dev/null +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -0,0 +1,74 @@ +import 'package:immich_mobile/domain/models/user.model.dart' as model; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/utils/hash.dart'; +import 'package:isar/isar.dart'; + +part 'user.entity.g.dart'; + +@Collection(inheritance: false) +class User { + Id get isarId => fastHash(id); + @Index(unique: true, replace: false, type: IndexType.hash) + final String id; + final DateTime updatedAt; + final String email; + final String name; + final bool isPartnerSharedBy; + final bool isPartnerSharedWith; + final bool isAdmin; + final String profileImagePath; + @Enumerated(EnumType.ordinal) + final model.AvatarColor avatarColor; + final bool memoryEnabled; + final bool inTimeline; + final int quotaUsageInBytes; + final int quotaSizeInBytes; + + const User({ + required this.id, + required this.updatedAt, + required this.email, + required this.name, + required this.isAdmin, + this.isPartnerSharedBy = false, + this.isPartnerSharedWith = false, + this.profileImagePath = '', + this.avatarColor = model.AvatarColor.primary, + this.memoryEnabled = true, + this.inTimeline = false, + this.quotaUsageInBytes = 0, + this.quotaSizeInBytes = 0, + }); + + static User fromDto(model.User dto) => User( + id: dto.uid, + updatedAt: dto.updatedAt, + email: dto.email, + name: dto.name, + isAdmin: dto.isAdmin, + isPartnerSharedBy: dto.isPartnerSharedBy, + isPartnerSharedWith: dto.isPartnerSharedWith, + profileImagePath: dto.profileImagePath ?? "", + avatarColor: dto.avatarColor, + memoryEnabled: dto.memoryEnabled, + inTimeline: dto.inTimeline, + quotaUsageInBytes: dto.quotaUsageInBytes, + quotaSizeInBytes: dto.quotaSizeInBytes, + ); + + model.User toDto() => model.User( + uid: id, + email: email, + name: name, + isAdmin: isAdmin, + updatedAt: updatedAt, + profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, + avatarColor: avatarColor, + memoryEnabled: memoryEnabled, + inTimeline: inTimeline, + isPartnerSharedBy: isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith, + quotaUsageInBytes: quotaUsageInBytes, + quotaSizeInBytes: quotaSizeInBytes, + ); +} diff --git a/mobile/lib/entities/user.entity.g.dart b/mobile/lib/infrastructure/entities/user.entity.g.dart similarity index 86% rename from mobile/lib/entities/user.entity.g.dart rename to mobile/lib/infrastructure/entities/user.entity.g.dart index a7aaee44bf..314ae97538 100644 --- a/mobile/lib/entities/user.entity.g.dart +++ b/mobile/lib/infrastructure/entities/user.entity.g.dart @@ -28,63 +28,58 @@ const UserSchema = CollectionSchema( name: r'email', type: IsarType.string, ), - r'hasQuota': PropertySchema( - id: 2, - name: r'hasQuota', - type: IsarType.bool, - ), r'id': PropertySchema( - id: 3, + id: 2, name: r'id', type: IsarType.string, ), r'inTimeline': PropertySchema( - id: 4, + id: 3, name: r'inTimeline', type: IsarType.bool, ), r'isAdmin': PropertySchema( - id: 5, + id: 4, name: r'isAdmin', type: IsarType.bool, ), r'isPartnerSharedBy': PropertySchema( - id: 6, + id: 5, name: r'isPartnerSharedBy', type: IsarType.bool, ), r'isPartnerSharedWith': PropertySchema( - id: 7, + id: 6, name: r'isPartnerSharedWith', type: IsarType.bool, ), r'memoryEnabled': PropertySchema( - id: 8, + id: 7, name: r'memoryEnabled', type: IsarType.bool, ), r'name': PropertySchema( - id: 9, + id: 8, name: r'name', type: IsarType.string, ), r'profileImagePath': PropertySchema( - id: 10, + id: 9, name: r'profileImagePath', type: IsarType.string, ), r'quotaSizeInBytes': PropertySchema( - id: 11, + id: 10, name: r'quotaSizeInBytes', type: IsarType.long, ), r'quotaUsageInBytes': PropertySchema( - id: 12, + id: 11, name: r'quotaUsageInBytes', type: IsarType.long, ), r'updatedAt': PropertySchema( - id: 13, + id: 12, name: r'updatedAt', type: IsarType.dateTime, ) @@ -109,22 +104,7 @@ const UserSchema = CollectionSchema( ], ) }, - links: { - r'albums': LinkSchema( - id: -8764917375410137318, - name: r'albums', - target: r'Album', - single: false, - linkName: r'owner', - ), - r'sharedAlbums': LinkSchema( - id: -7037628715076287024, - name: r'sharedAlbums', - target: r'Album', - single: false, - linkName: r'sharedUsers', - ) - }, + links: {}, embeddedSchemas: {}, getId: _userGetId, getLinks: _userGetLinks, @@ -153,18 +133,17 @@ void _userSerialize( ) { writer.writeByte(offsets[0], object.avatarColor.index); writer.writeString(offsets[1], object.email); - writer.writeBool(offsets[2], object.hasQuota); - writer.writeString(offsets[3], object.id); - writer.writeBool(offsets[4], object.inTimeline); - writer.writeBool(offsets[5], object.isAdmin); - writer.writeBool(offsets[6], object.isPartnerSharedBy); - writer.writeBool(offsets[7], object.isPartnerSharedWith); - writer.writeBool(offsets[8], object.memoryEnabled); - writer.writeString(offsets[9], object.name); - writer.writeString(offsets[10], object.profileImagePath); - writer.writeLong(offsets[11], object.quotaSizeInBytes); - writer.writeLong(offsets[12], object.quotaUsageInBytes); - writer.writeDateTime(offsets[13], object.updatedAt); + writer.writeString(offsets[2], object.id); + writer.writeBool(offsets[3], object.inTimeline); + writer.writeBool(offsets[4], object.isAdmin); + writer.writeBool(offsets[5], object.isPartnerSharedBy); + writer.writeBool(offsets[6], object.isPartnerSharedWith); + writer.writeBool(offsets[7], object.memoryEnabled); + writer.writeString(offsets[8], object.name); + writer.writeString(offsets[9], object.profileImagePath); + writer.writeLong(offsets[10], object.quotaSizeInBytes); + writer.writeLong(offsets[11], object.quotaUsageInBytes); + writer.writeDateTime(offsets[12], object.updatedAt); } User _userDeserialize( @@ -176,19 +155,19 @@ User _userDeserialize( final object = User( avatarColor: _UseravatarColorValueEnumMap[reader.readByteOrNull(offsets[0])] ?? - AvatarColorEnum.primary, + model.AvatarColor.primary, email: reader.readString(offsets[1]), - id: reader.readString(offsets[3]), - inTimeline: reader.readBoolOrNull(offsets[4]) ?? false, - isAdmin: reader.readBool(offsets[5]), - isPartnerSharedBy: reader.readBoolOrNull(offsets[6]) ?? false, - isPartnerSharedWith: reader.readBoolOrNull(offsets[7]) ?? false, - memoryEnabled: reader.readBoolOrNull(offsets[8]) ?? true, - name: reader.readString(offsets[9]), - profileImagePath: reader.readStringOrNull(offsets[10]) ?? '', - quotaSizeInBytes: reader.readLongOrNull(offsets[11]) ?? 0, - quotaUsageInBytes: reader.readLongOrNull(offsets[12]) ?? 0, - updatedAt: reader.readDateTime(offsets[13]), + id: reader.readString(offsets[2]), + inTimeline: reader.readBoolOrNull(offsets[3]) ?? false, + isAdmin: reader.readBool(offsets[4]), + isPartnerSharedBy: reader.readBoolOrNull(offsets[5]) ?? false, + isPartnerSharedWith: reader.readBoolOrNull(offsets[6]) ?? false, + memoryEnabled: reader.readBoolOrNull(offsets[7]) ?? true, + name: reader.readString(offsets[8]), + profileImagePath: reader.readStringOrNull(offsets[9]) ?? '', + quotaSizeInBytes: reader.readLongOrNull(offsets[10]) ?? 0, + quotaUsageInBytes: reader.readLongOrNull(offsets[11]) ?? 0, + updatedAt: reader.readDateTime(offsets[12]), ); return object; } @@ -202,32 +181,30 @@ P _userDeserializeProp

( switch (propertyId) { case 0: return (_UseravatarColorValueEnumMap[reader.readByteOrNull(offset)] ?? - AvatarColorEnum.primary) as P; + model.AvatarColor.primary) as P; case 1: return (reader.readString(offset)) as P; case 2: - return (reader.readBool(offset)) as P; - case 3: return (reader.readString(offset)) as P; - case 4: + case 3: return (reader.readBoolOrNull(offset) ?? false) as P; - case 5: + case 4: return (reader.readBool(offset)) as P; + case 5: + return (reader.readBoolOrNull(offset) ?? false) as P; case 6: return (reader.readBoolOrNull(offset) ?? false) as P; case 7: - return (reader.readBoolOrNull(offset) ?? false) as P; - case 8: return (reader.readBoolOrNull(offset) ?? true) as P; - case 9: + case 8: return (reader.readString(offset)) as P; - case 10: + case 9: return (reader.readStringOrNull(offset) ?? '') as P; + case 10: + return (reader.readLongOrNull(offset) ?? 0) as P; case 11: return (reader.readLongOrNull(offset) ?? 0) as P; case 12: - return (reader.readLongOrNull(offset) ?? 0) as P; - case 13: return (reader.readDateTime(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); @@ -247,16 +224,16 @@ const _UseravatarColorEnumValueMap = { 'amber': 9, }; const _UseravatarColorValueEnumMap = { - 0: AvatarColorEnum.primary, - 1: AvatarColorEnum.pink, - 2: AvatarColorEnum.red, - 3: AvatarColorEnum.yellow, - 4: AvatarColorEnum.blue, - 5: AvatarColorEnum.green, - 6: AvatarColorEnum.purple, - 7: AvatarColorEnum.orange, - 8: AvatarColorEnum.gray, - 9: AvatarColorEnum.amber, + 0: AvatarColor.primary, + 1: AvatarColor.pink, + 2: AvatarColor.red, + 3: AvatarColor.yellow, + 4: AvatarColor.blue, + 5: AvatarColor.green, + 6: AvatarColor.purple, + 7: AvatarColor.orange, + 8: AvatarColor.gray, + 9: AvatarColor.amber, }; Id _userGetId(User object) { @@ -264,14 +241,10 @@ Id _userGetId(User object) { } List> _userGetLinks(User object) { - return [object.albums, object.sharedAlbums]; + return []; } -void _userAttach(IsarCollection col, Id id, User object) { - object.albums.attach(col, col.isar.collection(), r'albums', id); - object.sharedAlbums - .attach(col, col.isar.collection(), r'sharedAlbums', id); -} +void _userAttach(IsarCollection col, Id id, User object) {} extension UserByIndex on IsarCollection { Future getById(String id) { @@ -447,7 +420,7 @@ extension UserQueryWhere on QueryBuilder { extension UserQueryFilter on QueryBuilder { QueryBuilder avatarColorEqualTo( - AvatarColorEnum value) { + AvatarColor value) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( property: r'avatarColor', @@ -457,7 +430,7 @@ extension UserQueryFilter on QueryBuilder { } QueryBuilder avatarColorGreaterThan( - AvatarColorEnum value, { + AvatarColor value, { bool include = false, }) { return QueryBuilder.apply(this, (query) { @@ -470,7 +443,7 @@ extension UserQueryFilter on QueryBuilder { } QueryBuilder avatarColorLessThan( - AvatarColorEnum value, { + AvatarColor value, { bool include = false, }) { return QueryBuilder.apply(this, (query) { @@ -483,8 +456,8 @@ extension UserQueryFilter on QueryBuilder { } QueryBuilder avatarColorBetween( - AvatarColorEnum lower, - AvatarColorEnum upper, { + AvatarColor lower, + AvatarColor upper, { bool includeLower = true, bool includeUpper = true, }) { @@ -627,15 +600,6 @@ extension UserQueryFilter on QueryBuilder { }); } - QueryBuilder hasQuotaEqualTo(bool value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hasQuota', - value: value, - )); - }); - } - QueryBuilder idEqualTo( String value, { bool caseSensitive = true, @@ -1285,118 +1249,7 @@ extension UserQueryFilter on QueryBuilder { extension UserQueryObject on QueryBuilder {} -extension UserQueryLinks on QueryBuilder { - QueryBuilder albums(FilterQuery q) { - return QueryBuilder.apply(this, (query) { - return query.link(q, r'albums'); - }); - } - - QueryBuilder albumsLengthEqualTo( - int length) { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'albums', length, true, length, true); - }); - } - - QueryBuilder albumsIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'albums', 0, true, 0, true); - }); - } - - QueryBuilder albumsIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'albums', 0, false, 999999, true); - }); - } - - QueryBuilder albumsLengthLessThan( - int length, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'albums', 0, true, length, include); - }); - } - - QueryBuilder albumsLengthGreaterThan( - int length, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'albums', length, include, 999999, true); - }); - } - - QueryBuilder albumsLengthBetween( - int lower, - int upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.linkLength( - r'albums', lower, includeLower, upper, includeUpper); - }); - } - - QueryBuilder sharedAlbums( - FilterQuery q) { - return QueryBuilder.apply(this, (query) { - return query.link(q, r'sharedAlbums'); - }); - } - - QueryBuilder sharedAlbumsLengthEqualTo( - int length) { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'sharedAlbums', length, true, length, true); - }); - } - - QueryBuilder sharedAlbumsIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'sharedAlbums', 0, true, 0, true); - }); - } - - QueryBuilder sharedAlbumsIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'sharedAlbums', 0, false, 999999, true); - }); - } - - QueryBuilder sharedAlbumsLengthLessThan( - int length, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'sharedAlbums', 0, true, length, include); - }); - } - - QueryBuilder sharedAlbumsLengthGreaterThan( - int length, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.linkLength(r'sharedAlbums', length, include, 999999, true); - }); - } - - QueryBuilder sharedAlbumsLengthBetween( - int lower, - int upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.linkLength( - r'sharedAlbums', lower, includeLower, upper, includeUpper); - }); - } -} +extension UserQueryLinks on QueryBuilder {} extension UserQuerySortBy on QueryBuilder { QueryBuilder sortByAvatarColor() { @@ -1423,18 +1276,6 @@ extension UserQuerySortBy on QueryBuilder { }); } - QueryBuilder sortByHasQuota() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'hasQuota', Sort.asc); - }); - } - - QueryBuilder sortByHasQuotaDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'hasQuota', Sort.desc); - }); - } - QueryBuilder sortById() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.asc); @@ -1593,18 +1434,6 @@ extension UserQuerySortThenBy on QueryBuilder { }); } - QueryBuilder thenByHasQuota() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'hasQuota', Sort.asc); - }); - } - - QueryBuilder thenByHasQuotaDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'hasQuota', Sort.desc); - }); - } - QueryBuilder thenById() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.asc); @@ -1764,12 +1593,6 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByHasQuota() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'hasQuota'); - }); - } - QueryBuilder distinctById( {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { @@ -1848,7 +1671,7 @@ extension UserQueryProperty on QueryBuilder { }); } - QueryBuilder avatarColorProperty() { + QueryBuilder avatarColorProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'avatarColor'); }); @@ -1860,12 +1683,6 @@ extension UserQueryProperty on QueryBuilder { }); } - QueryBuilder hasQuotaProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'hasQuota'); - }); - } - QueryBuilder idProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'id'); diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index 5cf6838ee1..01fdfa128c 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -1,9 +1,9 @@ import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:isar/isar.dart'; class IsarStoreRepository extends IsarDatabaseRepository @@ -78,7 +78,7 @@ class IsarStoreRepository extends IsarDatabaseRepository const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), - const (User) => await UserRepository(_db).getByDbId(entity.intValue!), + const (User) => await IsarUserRepository(_db).get(entity.intValue!), _ => null, } as T?; @@ -89,7 +89,7 @@ class IsarStoreRepository extends IsarDatabaseRepository const (bool) => ((value as bool) ? 1 : 0, null), const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), const (User) => ( - (await UserRepository(_db).update(value as User)).isarId, + (await IsarUserRepository(_db).update(value as User)).id, null, ), _ => throw UnsupportedError( diff --git a/mobile/lib/infrastructure/repositories/user.repository.dart b/mobile/lib/infrastructure/repositories/user.repository.dart new file mode 100644 index 0000000000..323295a748 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/user.repository.dart @@ -0,0 +1,80 @@ +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' + as entity; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:isar/isar.dart'; + +class IsarUserRepository extends IsarDatabaseRepository + implements IUserRepository { + final Isar _db; + const IsarUserRepository(super.db) : _db = db; + + @override + Future delete(List ids) async { + await transaction(() async { + await _db.users.deleteAll(ids); + }); + } + + @override + Future deleteAll() async { + await transaction(() async { + await _db.users.clear(); + }); + } + + @override + Future get(int id) async { + return (await _db.users.get(id))?.toDto(); + } + + @override + Future> getAll({SortUserBy? sortBy}) async { + return (await _db.users + .where() + .optional( + sortBy != null, + (query) => switch (sortBy!) { + SortUserBy.id => query.sortById(), + }, + ) + .findAll()) + .map((u) => u.toDto()) + .toList(); + } + + @override + Future getByUserId(String id) async { + return (await _db.users.getById(id))?.toDto(); + } + + @override + Future> getByUserIds(List ids) async { + return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList(); + } + + @override + Future insert(User user) async { + await transaction(() async { + await _db.users.put(entity.User.fromDto(user)); + }); + return true; + } + + @override + Future update(User user) async { + await transaction(() async { + await _db.users.put(entity.User.fromDto(user)); + }); + return user; + } + + @override + Future updateAll(List users) async { + await transaction(() async { + await _db.users.putAll(users.map(entity.User.fromDto).toList()); + }); + return true; + } +} diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart new file mode 100644 index 0000000000..6ed7844341 --- /dev/null +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -0,0 +1,66 @@ +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:openapi/api.dart'; + +abstract final class UserConverter { + /// Base user dto used where the complete user object is not required + static User fromSimpleUserDto(UserResponseDto dto) => User( + uid: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + profileImagePath: dto.profileImagePath, + avatarColor: dto.avatarColor.toAvatarColor(), + ); + + static User fromAdminDto( + UserAdminResponseDto adminDto, [ + UserPreferencesResponseDto? preferenceDto, + ]) => + User( + uid: adminDto.id, + email: adminDto.email, + name: adminDto.name, + isAdmin: adminDto.isAdmin, + updatedAt: adminDto.updatedAt, + profileImagePath: adminDto.profileImagePath, + avatarColor: adminDto.avatarColor.toAvatarColor(), + memoryEnabled: preferenceDto?.memories.enabled ?? true, + inTimeline: false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, + quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, + ); + + static User fromPartnerDto(PartnerResponseDto dto) => User( + uid: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + profileImagePath: dto.profileImagePath, + avatarColor: dto.avatarColor.toAvatarColor(), + memoryEnabled: false, + inTimeline: dto.inTimeline ?? false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + quotaUsageInBytes: 0, + quotaSizeInBytes: 0, + ); +} + +extension on UserAvatarColor { + AvatarColor toAvatarColor() => switch (this) { + UserAvatarColor.red => AvatarColor.red, + UserAvatarColor.green => AvatarColor.green, + UserAvatarColor.blue => AvatarColor.blue, + UserAvatarColor.purple => AvatarColor.purple, + UserAvatarColor.orange => AvatarColor.orange, + UserAvatarColor.pink => AvatarColor.pink, + UserAvatarColor.amber => AvatarColor.amber, + UserAvatarColor.yellow => AvatarColor.yellow, + UserAvatarColor.gray => AvatarColor.gray, + UserAvatarColor.primary || _ => AvatarColor.primary, + }; +} diff --git a/mobile/lib/interfaces/album.interface.dart b/mobile/lib/interfaces/album.interface.dart index 3a83a8feb7..3f903fb644 100644 --- a/mobile/lib/interfaces/album.interface.dart +++ b/mobile/lib/interfaces/album.interface.dart @@ -1,6 +1,6 @@ +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/interfaces/database.interface.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; diff --git a/mobile/lib/interfaces/partner.interface.dart b/mobile/lib/interfaces/partner.interface.dart index 995e07c392..3125bd9a9b 100644 --- a/mobile/lib/interfaces/partner.interface.dart +++ b/mobile/lib/interfaces/partner.interface.dart @@ -1,4 +1,4 @@ -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; abstract class IPartnerRepository { Future> getSharedWith(); diff --git a/mobile/lib/interfaces/partner_api.interface.dart b/mobile/lib/interfaces/partner_api.interface.dart index bca1baf66d..a91d305a78 100644 --- a/mobile/lib/interfaces/partner_api.interface.dart +++ b/mobile/lib/interfaces/partner_api.interface.dart @@ -1,4 +1,4 @@ -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; abstract interface class IPartnerApiRepository { Future> getAll(Direction direction); diff --git a/mobile/lib/interfaces/user.interface.dart b/mobile/lib/interfaces/user.interface.dart deleted file mode 100644 index d099e0e50b..0000000000 --- a/mobile/lib/interfaces/user.interface.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:immich_mobile/entities/user.entity.dart'; -import 'package:immich_mobile/interfaces/database.interface.dart'; - -abstract interface class IUserRepository implements IDatabaseRepository { - Future get(String id); - - Future getByDbId(int id); - - Future> getByIds(List ids); - - Future> getAll({bool self = true, UserSort? sortBy}); - - /// Returns all users whose assets can be accessed (self+partners) - Future> getAllAccessible(); - - Future> upsertAll(List users); - - Future update(User user); - - Future deleteById(List ids); - - Future me(); - - Future clearTable(); -} - -enum UserSort { id } diff --git a/mobile/lib/interfaces/user_api.interface.dart b/mobile/lib/interfaces/user_api.interface.dart index 67ac3c0883..65be1fceb9 100644 --- a/mobile/lib/interfaces/user_api.interface.dart +++ b/mobile/lib/interfaces/user_api.interface.dart @@ -1,6 +1,6 @@ import 'dart:typed_data'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; abstract interface class IUserApiRepository { Future> getAll(); diff --git a/mobile/lib/models/activities/activity.model.dart b/mobile/lib/models/activities/activity.model.dart index 4702753f41..8697246842 100644 --- a/mobile/lib/models/activities/activity.model.dart +++ b/mobile/lib/models/activities/activity.model.dart @@ -1,4 +1,4 @@ -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; enum ActivityType { comment, like } diff --git a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart index 02026b828d..0fc3dd20fb 100644 --- a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart @@ -3,11 +3,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/suggested_shared_users.provider.dart'; -import 'package:immich_mobile/entities/album.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; @RoutePage() @@ -151,7 +151,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { onData: (users) { for (var sharedUsers in album.sharedUsers) { users.removeWhere( - (u) => u.id == sharedUsers.id || u.id == album.ownerId, + (u) => u.uid == sharedUsers.id || u.uid == album.ownerId, ); } diff --git a/mobile/lib/pages/album/album_options.page.dart b/mobile/lib/pages/album/album_options.page.dart index 0e9bfeb2ce..7f8c1a9592 100644 --- a/mobile/lib/pages/album/album_options.page.dart +++ b/mobile/lib/pages/album/album_options.page.dart @@ -4,14 +4,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' + as entity; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; -import 'package:immich_mobile/utils/immich_loading_overlay.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/utils/immich_loading_overlay.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; @@ -26,7 +28,8 @@ class AlbumOptionsPage extends HookConsumerWidget { return const SizedBox(); } - final sharedUsers = useState(album.sharedUsers.toList()); + final sharedUsers = + useState(album.sharedUsers.map((u) => u.toDto()).toList()); final owner = album.owner.value; final userId = ref.watch(authProvider).userId; final activityEnabled = useState(album.activityEnabled); @@ -69,8 +72,8 @@ class AlbumOptionsPage extends HookConsumerWidget { try { await ref.read(albumProvider.notifier).removeUser(album, user); - album.sharedUsers.remove(user); - sharedUsers.value = album.sharedUsers.toList(); + album.sharedUsers.remove(entity.User.fromDto(user)); + sharedUsers.value = album.sharedUsers.map((u) => u.toDto()).toList(); } catch (error) { showErrorMessage(); } @@ -82,7 +85,7 @@ class AlbumOptionsPage extends HookConsumerWidget { void handleUserClick(User user) { var actions = []; - if (user.id == userId) { + if (user.uid == userId) { actions = [ ListTile( leading: const Icon(Icons.exit_to_app_rounded), @@ -123,8 +126,9 @@ class AlbumOptionsPage extends HookConsumerWidget { buildOwnerInfo() { return ListTile( - leading: - owner != null ? UserCircleAvatar(user: owner) : const SizedBox(), + leading: owner != null + ? UserCircleAvatar(user: owner.toDto()) + : const SizedBox(), title: Text( album.owner.value?.name ?? "", style: const TextStyle( @@ -166,10 +170,10 @@ class AlbumOptionsPage extends HookConsumerWidget { color: context.colorScheme.onSurfaceSecondary, ), ), - trailing: userId == user.id || isOwner + trailing: userId == user.uid || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(), - onTap: userId == user.id || isOwner + onTap: userId == user.uid || isOwner ? () => handleUserClick(user) : null, ); diff --git a/mobile/lib/pages/album/album_shared_user_icons.dart b/mobile/lib/pages/album/album_shared_user_icons.dart index f417f9fb38..7601ffd9fc 100644 --- a/mobile/lib/pages/album/album_shared_user_icons.dart +++ b/mobile/lib/pages/album/album_shared_user_icons.dart @@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; @@ -23,7 +23,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget { return sharedUsers.value; } - return album.sharedUsers.toList(growable: false); + return album.sharedUsers.map((u) => u.toDto()).toList(growable: false); }), ); diff --git a/mobile/lib/pages/album/album_shared_user_selection.page.dart b/mobile/lib/pages/album/album_shared_user_selection.page.dart index ed8a45194d..3bf5390f73 100644 --- a/mobile/lib/pages/album/album_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_shared_user_selection.page.dart @@ -3,14 +3,14 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/album_title.provider.dart'; import 'package:immich_mobile/providers/album/suggested_shared_users.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; @RoutePage() diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart index ac6bd2f2fb..39623ff076 100644 --- a/mobile/lib/pages/albums/albums.page.dart +++ b/mobile/lib/pages/albums/albums.page.dart @@ -33,7 +33,7 @@ class AlbumsPage extends HookConsumerWidget { final searchController = useTextEditingController(); final debounceTimer = useRef(null); final filterMode = useState(QuickFilterMode.all); - final userId = ref.watch(currentUserProvider)?.id; + final userId = ref.watch(currentUserProvider)?.uid; final searchFocusNode = useFocusNode(); toggleViewMode() { diff --git a/mobile/lib/pages/common/activities.page.dart b/mobile/lib/pages/common/activities.page.dart index 9678058111..84a8622fa5 100644 --- a/mobile/lib/pages/common/activities.page.dart +++ b/mobile/lib/pages/common/activities.page.dart @@ -7,12 +7,12 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/activities/activity.model.dart'; import 'package:immich_mobile/providers/activity.provider.dart'; -import 'package:immich_mobile/widgets/activities/activity_text_field.dart'; -import 'package:immich_mobile/widgets/activities/activity_tile.dart'; -import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/activities/activity_text_field.dart'; +import 'package:immich_mobile/widgets/activities/activity_tile.dart'; +import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; @RoutePage() class ActivitiesPage extends HookConsumerWidget { @@ -72,7 +72,7 @@ class ActivitiesPage extends HookConsumerWidget { final activity = data[index]; final canDelete = activity.user.id == user?.id || - album.ownerId == user?.id; + album.ownerId == user?.uid; return Padding( padding: const EdgeInsets.all(5), diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index 31b465ead7..9dfa4e1b9f 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; diff --git a/mobile/lib/pages/library/partner/partner.page.dart b/mobile/lib/pages/library/partner/partner.page.dart index 1e9e801210..914c7cd9de 100644 --- a/mobile/lib/pages/library/partner/partner.page.dart +++ b/mobile/lib/pages/library/partner/partner.page.dart @@ -2,10 +2,10 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/partner.provider.dart'; import 'package:immich_mobile/services/partner.service.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/user_avatar.dart'; diff --git a/mobile/lib/pages/library/partner/partner_detail.page.dart b/mobile/lib/pages/library/partner/partner_detail.page.dart index f018726fe2..3230926308 100644 --- a/mobile/lib/pages/library/partner/partner_detail.page.dart +++ b/mobile/lib/pages/library/partner/partner_detail.page.dart @@ -2,11 +2,11 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/multiselect.provider.dart'; import 'package:immich_mobile/providers/partner.provider.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; -import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -111,7 +111,7 @@ class PartnerDetailPage extends HookConsumerWidget { ), ), ), - renderListProvider: singleUserTimelineProvider(partner.isarId), + renderListProvider: singleUserTimelineProvider(partner.id), onRefresh: () => ref.read(assetProvider.notifier).getAllAsset(), deleteEnabled: false, favoriteEnabled: false, diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart index b3bfa366f2..c9211e984d 100644 --- a/mobile/lib/pages/photos/photos.page.dart +++ b/mobile/lib/pages/photos/photos.page.dart @@ -7,16 +7,16 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; -import 'package:immich_mobile/providers/multiselect.provider.dart'; -import 'package:immich_mobile/providers/timeline.provider.dart'; -import 'package:immich_mobile/widgets/memories/memory_lane.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; +import 'package:immich_mobile/providers/multiselect.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart'; import 'package:immich_mobile/widgets/common/immich_app_bar.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; +import 'package:immich_mobile/widgets/memories/memory_lane.dart'; @RoutePage() class PhotosPage extends HookConsumerWidget { @@ -110,7 +110,7 @@ class PhotosPage extends HookConsumerWidget { : const SizedBox(), renderListProvider: timelineUsers.length > 1 ? multiUsersTimelineProvider(timelineUsers) - : singleUserTimelineProvider(currentUser?.isarId), + : singleUserTimelineProvider(currentUser?.id), buildLoadingIndicator: buildLoadingIndicator, onRefresh: refreshAssets, stackEnabled: true, diff --git a/mobile/lib/providers/album/album.provider.dart b/mobile/lib/providers/album/album.provider.dart index a2d7db68ec..7450a0f8e3 100644 --- a/mobile/lib/providers/album/album.provider.dart +++ b/mobile/lib/providers/album/album.provider.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/services/album.service.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/album.entity.dart'; final isRefreshingRemoteAlbumProvider = StateProvider((ref) => false); diff --git a/mobile/lib/providers/album/suggested_shared_users.provider.dart b/mobile/lib/providers/album/suggested_shared_users.provider.dart index fe8a1fccce..1f00bc5cb7 100644 --- a/mobile/lib/providers/album/suggested_shared_users.provider.dart +++ b/mobile/lib/providers/album/suggested_shared_users.provider.dart @@ -1,9 +1,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/user.service.dart'; -final otherUsersProvider = FutureProvider.autoDispose>((ref) { +final otherUsersProvider = FutureProvider.autoDispose>((ref) async { UserService userService = ref.watch(userServiceProvider); + final currentUser = ref.watch(currentUserProvider); - return userService.getUsers(); + final allUsers = await userService.getAll(); + allUsers.removeWhere((u) => currentUser?.id == u.id); + return allUsers; }); diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index e2939e89ce..d2e3d6e9d2 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -2,8 +2,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_udid/flutter_udid.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/models/auth/auth_state.model.dart'; import 'package:immich_mobile/models/auth/login_response.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; @@ -141,18 +142,18 @@ class AuthNotifier extends StateNotifier { // If the user information is successfully retrieved, update the store // Due to the flow of the code, this will always happen on first login - if (userResponse != null) { + if (userResponse == null) { + _log.severe("Unable to get user information from the server."); + } else { await Store.put(StoreKey.deviceId, deviceId); await Store.put(StoreKey.deviceIdHash, fastHash(deviceId)); await Store.put( StoreKey.currentUser, - User.fromUserDto(userResponse, userPreferences), + UserConverter.fromAdminDto(userResponse, userPreferences), ); await Store.put(StoreKey.accessToken, accessToken); - user = User.fromUserDto(userResponse, userPreferences); - } else { - _log.severe("Unable to get user information from the server."); + user = UserConverter.fromAdminDto(userResponse, userPreferences); } // If the user is null, the login was not successful @@ -163,7 +164,7 @@ class AuthNotifier extends StateNotifier { state = state.copyWith( isAuthenticated: true, - userId: user.id, + userId: user.uid, userEmail: user.email, name: user.name, profileImagePath: user.profileImagePath, diff --git a/mobile/lib/providers/infrastructure/store.provider.dart b/mobile/lib/providers/infrastructure/store.provider.dart index 2712208e76..c0de937deb 100644 --- a/mobile/lib/providers/infrastructure/store.provider.dart +++ b/mobile/lib/providers/infrastructure/store.provider.dart @@ -1,11 +1,14 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/interfaces/store.interface.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'store.provider.g.dart'; -@riverpod -IStoreRepository storeRepository(Ref ref) => +@Riverpod(keepAlive: true) +IStoreRepository storeRepository(StoreRepositoryRef ref) => IsarStoreRepository(ref.watch(isarProvider)); + +@Riverpod(keepAlive: true) +StoreService storeService(StoreServiceRef _) => StoreService.I; diff --git a/mobile/lib/providers/infrastructure/store.provider.g.dart b/mobile/lib/providers/infrastructure/store.provider.g.dart index 0551793b80..712f5351c9 100644 --- a/mobile/lib/providers/infrastructure/store.provider.g.dart +++ b/mobile/lib/providers/infrastructure/store.provider.g.dart @@ -6,11 +6,11 @@ part of 'store.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$storeRepositoryHash() => r'2f1c3e2e2db5082a40eb30a183a6c770f5b09d76'; +String _$storeRepositoryHash() => r'9bf5fb8aebbe4439078d6507671efa900e8f13b7'; /// See also [storeRepository]. @ProviderFor(storeRepository) -final storeRepositoryProvider = AutoDisposeProvider.internal( +final storeRepositoryProvider = Provider.internal( storeRepository, name: r'storeRepositoryProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -20,8 +20,20 @@ final storeRepositoryProvider = AutoDisposeProvider.internal( allTransitiveDependencies: null, ); -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef StoreRepositoryRef = AutoDisposeProviderRef; +typedef StoreRepositoryRef = ProviderRef; +String _$storeServiceHash() => r'a072a39c42ad1a4d1133706f408c89c2415ba35e'; + +/// See also [storeService]. +@ProviderFor(storeService) +final storeServiceProvider = Provider.internal( + storeService, + name: r'storeServiceProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$storeServiceHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef StoreServiceRef = ProviderRef; // ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/mobile/lib/providers/infrastructure/user.provider.dart b/mobile/lib/providers/infrastructure/user.provider.dart new file mode 100644 index 0000000000..36af9b9cc0 --- /dev/null +++ b/mobile/lib/providers/infrastructure/user.provider.dart @@ -0,0 +1,10 @@ +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'user.provider.g.dart'; + +@Riverpod(keepAlive: true) +IUserRepository userRepository(UserRepositoryRef ref) => + IsarUserRepository(ref.watch(isarProvider)); diff --git a/mobile/lib/providers/infrastructure/user.provider.g.dart b/mobile/lib/providers/infrastructure/user.provider.g.dart new file mode 100644 index 0000000000..00c5461491 --- /dev/null +++ b/mobile/lib/providers/infrastructure/user.provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user.provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$userRepositoryHash() => r'17ad0964faccd58f1e2571452055e74613f6da18'; + +/// See also [userRepository]. +@ProviderFor(userRepository) +final userRepositoryProvider = Provider.internal( + userRepository, + name: r'userRepositoryProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$userRepositoryHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef UserRepositoryRef = ProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/mobile/lib/providers/partner.provider.dart b/mobile/lib/providers/partner.provider.dart index 282e779432..fbd1a1b69f 100644 --- a/mobile/lib/providers/partner.provider.dart +++ b/mobile/lib/providers/partner.provider.dart @@ -2,9 +2,9 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/providers/album/suggested_shared_users.provider.dart'; import 'package:immich_mobile/services/partner.service.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; class PartnerSharedWithNotifier extends StateNotifier> { final PartnerService _partnerService; diff --git a/mobile/lib/providers/user.provider.dart b/mobile/lib/providers/user.provider.dart index 0a1bc0275a..ecd5ecd318 100644 --- a/mobile/lib/providers/user.provider.dart +++ b/mobile/lib/providers/user.provider.dart @@ -2,8 +2,9 @@ import 'dart:async'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; @@ -25,7 +26,7 @@ class CurrentUserProvider extends StateNotifier { if (user != null) { await Store.put( StoreKey.currentUser, - User.fromUserDto(user, userPreferences), + UserConverter.fromAdminDto(user, userPreferences), ); } } catch (_) {} diff --git a/mobile/lib/repositories/activity_api.repository.dart b/mobile/lib/repositories/activity_api.repository.dart index 8da3759709..868415caf9 100644 --- a/mobile/lib/repositories/activity_api.repository.dart +++ b/mobile/lib/repositories/activity_api.repository.dart @@ -1,5 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/interfaces/activity_api.interface.dart'; import 'package:immich_mobile/models/activities/activity.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; @@ -60,7 +60,7 @@ class ActivityApiRepository extends ApiRepository type: dto.type == ReactionType.comment ? ActivityType.comment : ActivityType.like, - user: User.fromSimpleUserDto(dto.user), + user: UserConverter.fromSimpleUserDto(dto.user), assetId: dto.assetId, comment: dto.comment, ); diff --git a/mobile/lib/repositories/album.repository.dart b/mobile/lib/repositories/album.repository.dart index 1d2df89579..35e6b4b7bd 100644 --- a/mobile/lib/repositories/album.repository.dart +++ b/mobile/lib/repositories/album.repository.dart @@ -1,9 +1,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' + as entity; import 'package:immich_mobile/interfaces/album.interface.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; @@ -43,11 +45,11 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { } if (owner == true) { query = query.owner( - (q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).isarId), + (q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).id), ); } else if (owner == false) { query = query.owner( - (q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).isarId), + (q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).id), ); } if (remote == true) { @@ -100,8 +102,9 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { Future get(int id) => db.albums.get(id); @override - Future removeUsers(Album album, List users) => - txn(() => album.sharedUsers.update(unlink: users)); + Future removeUsers(Album album, List users) => txn( + () => album.sharedUsers.update(unlink: users.map(entity.User.fromDto)), + ); @override Future addAssets(Album album, List assets) => @@ -122,7 +125,7 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { @override Future addUsers(Album album, List users) => - txn(() => album.sharedUsers.update(link: users)); + txn(() => album.sharedUsers.update(link: users.map(entity.User.fromDto))); @override Future deleteAllLocal() => @@ -141,11 +144,11 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { switch (filterMode) { case QuickFilterMode.sharedWithMe: query = query.owner( - (q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).isarId), + (q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).id), ); case QuickFilterMode.myAlbums: query = query.owner( - (q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).isarId), + (q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).id), ); case QuickFilterMode.all: break; diff --git a/mobile/lib/repositories/album_api.repository.dart b/mobile/lib/repositories/album_api.repository.dart index 2438304158..a7bbe452e6 100644 --- a/mobile/lib/repositories/album_api.repository.dart +++ b/mobile/lib/repositories/album_api.repository.dart @@ -2,7 +2,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' + as entity; +import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/interfaces/album_api.interface.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; @@ -164,11 +166,12 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository { sortOrder: dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc, ); album.remoteAssetCount = dto.assetCount; - album.owner.value = User.fromSimpleUserDto(dto.owner); + album.owner.value = + entity.User.fromDto(UserConverter.fromSimpleUserDto(dto.owner)); album.remoteThumbnailAssetId = dto.albumThumbnailAssetId; final users = dto.albumUsers - .map((albumUser) => User.fromSimpleUserDto(albumUser.user)); - album.sharedUsers.addAll(users); + .map((albumUser) => UserConverter.fromSimpleUserDto(albumUser.user)); + album.sharedUsers.addAll(users.map(entity.User.fromDto)); final assets = dto.assets.map(Asset.remote).toList(); album.assets.addAll(assets); return album; diff --git a/mobile/lib/repositories/album_media.repository.dart b/mobile/lib/repositories/album_media.repository.dart index f4f31cf14e..f08322e20a 100644 --- a/mobile/lib/repositories/album_media.repository.dart +++ b/mobile/lib/repositories/album_media.repository.dart @@ -3,6 +3,7 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/interfaces/album_media.interface.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; @@ -86,7 +87,7 @@ class AlbumMediaRepository implements IAlbumMediaRepository { shared: false, activityEnabled: false, ); - album.owner.value = Store.get(StoreKey.currentUser); + album.owner.value = User.fromDto(Store.get(StoreKey.currentUser)); album.localId = assetPathEntity.id; album.isAll = assetPathEntity.isAll; return album; diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart index 97d22f3600..0149a8d6c6 100644 --- a/mobile/lib/repositories/asset_media.repository.dart +++ b/mobile/lib/repositories/asset_media.repository.dart @@ -24,7 +24,7 @@ class AssetMediaRepository implements IAssetMediaRepository { final Asset asset = Asset( checksum: "", localId: local.id, - ownerId: Store.get(StoreKey.currentUser).isarId, + ownerId: Store.get(StoreKey.currentUser).id, fileCreatedAt: local.createDateTime, fileModifiedAt: local.modifiedDateTime, updatedAt: local.modifiedDateTime, diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index 18e14865aa..f9e82e1635 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -6,8 +6,8 @@ import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/interfaces/auth.interface.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; diff --git a/mobile/lib/repositories/partner.repository.dart b/mobile/lib/repositories/partner.repository.dart index cae49fee39..5f8723ccae 100644 --- a/mobile/lib/repositories/partner.repository.dart +++ b/mobile/lib/repositories/partner.repository.dart @@ -1,5 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' + as entity; import 'package:immich_mobile/interfaces/partner.interface.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; @@ -14,34 +16,40 @@ class PartnerRepository extends DatabaseRepository PartnerRepository(super.db); @override - Future> getSharedBy() { - return db.users - .filter() - .isPartnerSharedByEqualTo(true) - .sortById() - .findAll(); + Future> getSharedBy() async { + return (await db.users + .filter() + .isPartnerSharedByEqualTo(true) + .sortById() + .findAll()) + .map((u) => u.toDto()) + .toList(); } @override - Future> getSharedWith() { - return db.users - .filter() - .isPartnerSharedWithEqualTo(true) - .sortById() - .findAll(); + Future> getSharedWith() async { + return (await db.users + .filter() + .isPartnerSharedWithEqualTo(true) + .sortById() + .findAll()) + .map((u) => u.toDto()) + .toList(); } @override Stream> watchSharedBy() { - return db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch(); + return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()) + .map((users) => users.map((u) => u.toDto()).toList()); } @override Stream> watchSharedWith() { - return db.users - .filter() - .isPartnerSharedWithEqualTo(true) - .sortById() - .watch(); + return (db.users + .filter() + .isPartnerSharedWithEqualTo(true) + .sortById() + .watch()) + .map((users) => users.map((u) => u.toDto()).toList()); } } diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart index 1ae16d9d52..288644ab22 100644 --- a/mobile/lib/repositories/partner_api.repository.dart +++ b/mobile/lib/repositories/partner_api.repository.dart @@ -1,5 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/interfaces/partner_api.interface.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; @@ -26,13 +27,13 @@ class PartnerApiRepository extends ApiRepository : PartnerDirection.with_, ), ); - return response.map(User.fromPartnerDto).toList(); + return response.map(UserConverter.fromPartnerDto).toList(); } @override Future create(String id) async { final dto = await checkNull(_api.createPartner(id)); - return User.fromPartnerDto(dto); + return UserConverter.fromPartnerDto(dto); } @override @@ -46,6 +47,6 @@ class PartnerApiRepository extends ApiRepository UpdatePartnerDto(inTimeline: inTimeline), ), ); - return User.fromPartnerDto(dto); + return UserConverter.fromPartnerDto(dto); } } diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart index 1b9ee8ad37..1b0471059f 100644 --- a/mobile/lib/repositories/timeline.repository.dart +++ b/mobile/lib/repositories/timeline.repository.dart @@ -2,7 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/interfaces/timeline.interface.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; diff --git a/mobile/lib/repositories/user.repository.dart b/mobile/lib/repositories/user.repository.dart deleted file mode 100644 index ea67b30e0d..0000000000 --- a/mobile/lib/repositories/user.repository.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; -import 'package:immich_mobile/providers/db.provider.dart'; -import 'package:immich_mobile/repositories/database.repository.dart'; -import 'package:isar/isar.dart'; - -final userRepositoryProvider = - Provider((ref) => UserRepository(ref.watch(dbProvider))); - -class UserRepository extends DatabaseRepository implements IUserRepository { - UserRepository(super.db); - - @override - Future> getByIds(List ids) async => - (await db.users.getAllById(ids)).nonNulls.toList(); - - @override - Future get(String id) => db.users.getById(id); - - @override - Future> getAll({bool self = true, UserSort? sortBy}) { - final baseQuery = db.users.where(); - final int userId = Store.get(StoreKey.currentUser).isarId; - final QueryBuilder afterWhere = - self ? baseQuery.noOp() : baseQuery.isarIdNotEqualTo(userId); - final QueryBuilder query = switch (sortBy) { - null => afterWhere.noOp(), - UserSort.id => afterWhere.sortById(), - }; - return query.findAll(); - } - - @override - Future update(User user) async { - await txn(() => db.users.put(user)); - return user; - } - - @override - Future me() => Future.value(Store.get(StoreKey.currentUser)); - - @override - Future deleteById(List ids) => txn(() => db.users.deleteAll(ids)); - - @override - Future> upsertAll(List users) async { - await txn(() => db.users.putAll(users)); - return users; - } - - @override - Future> getAllAccessible() => db.users - .filter() - .isPartnerSharedWithEqualTo(true) - .or() - .isarIdEqualTo(Store.get(StoreKey.currentUser).isarId) - .findAll(); - - @override - Future getByDbId(int id) async { - return await db.users.get(id); - } - - @override - Future clearTable() async { - await txn(() async { - await db.users.clear(); - }); - } -} diff --git a/mobile/lib/repositories/user_api.repository.dart b/mobile/lib/repositories/user_api.repository.dart index 9641c4e0e6..cd52bdcbe9 100644 --- a/mobile/lib/repositories/user_api.repository.dart +++ b/mobile/lib/repositories/user_api.repository.dart @@ -2,7 +2,8 @@ import 'dart:typed_data'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:http/http.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/interfaces/user_api.interface.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; @@ -22,7 +23,7 @@ class UserApiRepository extends ApiRepository implements IUserApiRepository { @override Future> getAll() async { final dto = await checkNull(_api.searchUsers()); - return dto.map(User.fromSimpleUserDto).toList(); + return dto.map(UserConverter.fromSimpleUserDto).toList(); } @override diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index cd7a6f6b98..d7edc6fd28 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -2,11 +2,10 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/models/folder/recursive_folder.model.dart'; -import 'package:immich_mobile/pages/library/folder/folder.page.dart'; import 'package:immich_mobile/models/memories/memory.model.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/models/shared_link/shared_link.model.dart'; @@ -37,6 +36,7 @@ import 'package:immich_mobile/pages/editing/edit.page.dart'; import 'package:immich_mobile/pages/editing/filter.page.dart'; import 'package:immich_mobile/pages/library/archive.page.dart'; import 'package:immich_mobile/pages/library/favorite.page.dart'; +import 'package:immich_mobile/pages/library/folder/folder.page.dart'; import 'package:immich_mobile/pages/library/library.page.dart'; import 'package:immich_mobile/pages/library/local_albums.page.dart'; import 'package:immich_mobile/pages/library/partner/partner.page.dart'; diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart index b6a845a0b3..edbfe6da4c 100644 --- a/mobile/lib/routing/tab_navigation_observer.dart +++ b/mobile/lib/routing/tab_navigation_observer.dart @@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/memory.provider.dart'; @@ -39,7 +39,7 @@ class TabNavigationObserver extends AutoRouterObserver { await Store.put( StoreKey.currentUser, - User.fromUserDto(userResponseDto, userPreferences), + UserConverter.fromAdminDto(userResponseDto, userPreferences), ); ref.read(serverInfoProvider.notifier).getServerVersion(); } catch (e) { diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index 3a44ca7286..bdb98c621d 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -7,11 +7,13 @@ import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' + as entity; import 'package:immich_mobile/interfaces/album.interface.dart'; import 'package:immich_mobile/interfaces/album_api.interface.dart'; import 'package:immich_mobile/interfaces/album_media.interface.dart'; @@ -207,7 +209,7 @@ class AlbumService { final Album album = await _albumApiRepository.create( albumName, assetIds: assets.map((asset) => asset.remoteId!), - sharedUserIds: sharedUsers.map((user) => user.id), + sharedUserIds: sharedUsers.map((user) => user.uid), ); await _entityService.fillAlbumWithDatabaseEntities(album); return _albumRepository.create(album); @@ -294,7 +296,7 @@ class AlbumService { Future deleteAlbum(Album album) async { try { - final userId = Store.get(StoreKey.currentUser).isarId; + final userId = Store.get(StoreKey.currentUser).id; if (album.owner.value?.isarId == userId) { await _albumApiRepository.delete(album.remoteId!); } @@ -361,10 +363,10 @@ class AlbumService { try { await _albumApiRepository.removeUser( album.remoteId!, - userId: user.id, + userId: user.uid, ); - album.sharedUsers.remove(user); + album.sharedUsers.remove(entity.User.fromDto(user)); await _albumRepository.removeUsers(album, [user]); final a = await _albumRepository.get(album.id); // trigger watcher @@ -388,7 +390,10 @@ class AlbumService { album.sharedUsers.addAll(updatedAlbum.remoteUsers); album.shared = true; - await _albumRepository.addUsers(album, album.sharedUsers.toList()); + await _albumRepository.addUsers( + album, + album.sharedUsers.map((u) => u.toDto()).toList(), + ); await _albumRepository.update(album); return true; diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index 815962efac..5815ff4406 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -5,24 +5,27 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/interfaces/asset_api.interface.dart'; import 'package:immich_mobile/interfaces/asset_media.interface.dart'; import 'package:immich_mobile/interfaces/backup_album.interface.dart'; import 'package:immich_mobile/interfaces/etag.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:immich_mobile/repositories/backup.repository.dart'; import 'package:immich_mobile/repositories/etag.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/backup.service.dart'; @@ -45,6 +48,7 @@ final assetServiceProvider = Provider( ref.watch(userServiceProvider), ref.watch(backupServiceProvider), ref.watch(albumServiceProvider), + ref.watch(storeServiceProvider), ref.watch(assetMediaRepositoryProvider), ), ); @@ -61,6 +65,7 @@ class AssetService { final UserService _userService; final BackupService _backupService; final AlbumService _albumService; + final StoreService _storeService; final IAssetMediaRepository _assetMediaRepository; final log = Logger('AssetService'); @@ -76,6 +81,7 @@ class AssetService { this._userService, this._backupService, this._albumService, + this._storeService, this._assetMediaRepository, ); @@ -85,7 +91,7 @@ class AssetService { final syncedUserIds = await _etagRepository.getAllIds(); final List syncedUsers = syncedUserIds.isEmpty ? [] - : await _userRepository.getByIds(syncedUserIds); + : (await _userRepository.getByUserIds(syncedUserIds)).nonNulls.toList(); final Stopwatch sw = Stopwatch()..start(); final bool changes = await _syncService.syncRemoteAssetsToDb( users: syncedUsers, @@ -102,7 +108,7 @@ class AssetService { _getRemoteAssetChanges(List users, DateTime since) async { final dto = AssetDeltaSyncDto( updatedAfter: since, - userIds: users.map((e) => e.id).toList(), + userIds: users.map((e) => e.uid).toList(), ); final changes = await _apiService.syncApi.getDeltaSync(dto); return changes == null || changes.needsFullSync @@ -143,7 +149,7 @@ class AssetService { limit: chunkSize, updatedUntil: until, lastId: lastId, - userId: user.id, + userId: user.uid, ); log.fine("Requesting $chunkSize assets from $lastId"); final List? assets = @@ -314,9 +320,9 @@ class AssetService { ); await refreshRemoteAssets(); - final owner = await _userRepository.me(); + final owner = _storeService.get(StoreKey.currentUser); final remoteAssets = await _assetRepository.getAll( - ownerId: owner.isarId, + ownerId: owner.id, state: AssetState.merged, ); @@ -519,13 +525,13 @@ class AssetService { return _assetRepository.watchAsset(id, fireImmediately: fireImmediately); } - Future> getRecentlyAddedAssets() async { - final me = await _userRepository.me(); - return _assetRepository.getRecentlyAddedAssets(me.isarId); + Future> getRecentlyAddedAssets() { + final me = _storeService.get(StoreKey.currentUser); + return _assetRepository.getRecentlyAddedAssets(me.id); } - Future> getMotionAssets() async { - final me = await _userRepository.me(); - return _assetRepository.getMotionAssets(me.isarId); + Future> getMotionAssets() { + final me = _storeService.get(StoreKey.currentUser); + return _assetRepository.getMotionAssets(me.id); } } diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 548e4b1ad5..2025f439a1 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -12,10 +12,13 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:immich_mobile/interfaces/backup_album.interface.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; @@ -34,7 +37,6 @@ import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/repositories/network.repository.dart'; import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/repositories/permission.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; import 'package:immich_mobile/repositories/user_api.repository.dart'; import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/api.service.dart'; @@ -385,7 +387,7 @@ class BackgroundService { AlbumMediaRepository albumMediaRepository = AlbumMediaRepository(); FileMediaRepository fileMediaRepository = FileMediaRepository(); AssetMediaRepository assetMediaRepository = AssetMediaRepository(); - UserRepository userRepository = UserRepository(db); + IUserRepository userRepository = IsarUserRepository(db); UserApiRepository userApiRepository = UserApiRepository(apiService.usersApi); AlbumApiRepository albumApiRepository = @@ -405,6 +407,7 @@ class BackgroundService { assetRepository, exifInfoRepository, userRepository, + StoreService.I, eTagRepository, ); UserService userService = UserService( diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index c2e93a678a..e4d5ab4afd 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -34,7 +34,7 @@ class BackupVerificationService { /// Returns at most [limit] assets that were backed up without exif Future> findWronglyBackedUpAssets({int limit = 100}) async { - final owner = Store.get(StoreKey.currentUser).isarId; + final owner = Store.get(StoreKey.currentUser).id; final List onlyLocal = await _assetRepository.getAll( ownerId: owner, state: AssetState.local, diff --git a/mobile/lib/services/entity.service.dart b/mobile/lib/services/entity.service.dart index ddbe77f8c9..9e61366b94 100644 --- a/mobile/lib/services/entity.service.dart +++ b/mobile/lib/services/entity.service.dart @@ -1,9 +1,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; class EntityService { final IAssetRepository _assetRepository; @@ -17,7 +18,8 @@ class EntityService { final ownerId = album.ownerId; if (ownerId != null) { // replace owner with user from database - album.owner.value = await _userRepository.get(ownerId); + final user = await _userRepository.getByUserId(ownerId); + album.owner.value = user == null ? null : User.fromDto(user); } final thumbnailAssetId = album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId; @@ -29,9 +31,9 @@ class EntityService { if (album.remoteUsers.isNotEmpty) { // replace all users with users from database final users = await _userRepository - .getByIds(album.remoteUsers.map((user) => user.id).toList()); + .getByUserIds(album.remoteUsers.map((user) => user.id).toList()); album.sharedUsers.clear(); - album.sharedUsers.addAll(users); + album.sharedUsers.addAll(users.nonNulls.map(User.fromDto)); album.shared = true; } if (album.remoteAssets.isNotEmpty) { diff --git a/mobile/lib/services/partner.service.dart b/mobile/lib/services/partner.service.dart index 6bd429b51d..b0ab5b3437 100644 --- a/mobile/lib/services/partner.service.dart +++ b/mobile/lib/services/partner.service.dart @@ -1,11 +1,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/interfaces/partner.interface.dart'; import 'package:immich_mobile/interfaces/partner_api.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/partner.repository.dart'; import 'package:immich_mobile/repositories/partner_api.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; import 'package:logging/logging.dart'; final partnerServiceProvider = Provider( @@ -46,11 +46,10 @@ class PartnerService { Future removePartner(User partner) async { try { - await _partnerApiRepository.delete(partner.id); - partner.isPartnerSharedBy = false; - await _userRepository.update(partner); + await _partnerApiRepository.delete(partner.uid); + await _userRepository.update(partner.copyWith(isPartnerSharedBy: false)); } catch (e) { - _log.warning("Failed to remove partner ${partner.id}", e); + _log.warning("Failed to remove partner ${partner.uid}", e); return false; } return true; @@ -58,12 +57,11 @@ class PartnerService { Future addPartner(User partner) async { try { - await _partnerApiRepository.create(partner.id); - partner.isPartnerSharedBy = true; - await _userRepository.update(partner); + await _partnerApiRepository.create(partner.uid); + await _userRepository.update(partner.copyWith(isPartnerSharedBy: true)); return true; } catch (e) { - _log.warning("Failed to add partner ${partner.id}", e); + _log.warning("Failed to add partner ${partner.uid}", e); } return false; } @@ -71,14 +69,14 @@ class PartnerService { Future updatePartner(User partner, {required bool inTimeline}) async { try { final dto = await _partnerApiRepository.update( - partner.id, + partner.uid, inTimeline: inTimeline, ); - partner.inTimeline = dto.inTimeline; - await _userRepository.update(partner); + await _userRepository + .update(partner.copyWith(inTimeline: dto.inTimeline)); return true; } catch (e) { - _log.warning("Failed to update partner ${partner.id}", e); + _log.warning("Failed to update partner ${partner.uid}", e); } return false; } diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index b937dde320..f9404cb6d4 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -3,24 +3,27 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart'; import 'package:immich_mobile/interfaces/album.interface.dart'; import 'package:immich_mobile/interfaces/album_api.interface.dart'; import 'package:immich_mobile/interfaces/album_media.interface.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/interfaces/etag.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/album.repository.dart'; import 'package:immich_mobile/repositories/album_api.repository.dart'; import 'package:immich_mobile/repositories/album_media.repository.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/repositories/etag.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; import 'package:immich_mobile/services/entity.service.dart'; import 'package:immich_mobile/services/hash.service.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; @@ -38,6 +41,7 @@ final syncServiceProvider = Provider( ref.watch(assetRepositoryProvider), ref.watch(exifRepositoryProvider), ref.watch(userRepositoryProvider), + ref.watch(storeServiceProvider), ref.watch(etagRepositoryProvider), ), ); @@ -51,6 +55,7 @@ class SyncService { final IAssetRepository _assetRepository; final IExifInfoRepository _exifInfoRepository; final IUserRepository _userRepository; + final StoreService _storeService; final IETagRepository _eTagRepository; final AsyncMutex _lock = AsyncMutex(); final Logger _log = Logger('SyncService'); @@ -64,6 +69,7 @@ class SyncService { this._assetRepository, this._exifInfoRepository, this._userRepository, + this._storeService, this._eTagRepository, ); @@ -135,8 +141,8 @@ class SyncService { /// Syncs users from the server to the local database /// Returns `true`if there were any changes Future _syncUsersFromServer(List users) async { - users.sortBy((u) => u.id); - final dbUsers = await _userRepository.getAll(sortBy: UserSort.id); + users.sortBy((u) => u.uid); + final dbUsers = await _userRepository.getAll(sortBy: SortUserBy.id); final List toDelete = []; final List toUpsert = []; final changes = diffSortedListsSync( @@ -154,12 +160,12 @@ class SyncService { return false; }, onlyFirst: (User a) => toUpsert.add(a), - onlySecond: (User b) => toDelete.add(b.isarId), + onlySecond: (User b) => toDelete.add(b.id), ); if (changes) { await _userRepository.transaction(() async { - await _userRepository.deleteById(toDelete); - await _userRepository.upsertAll(toUpsert); + await _userRepository.delete(toDelete); + await _userRepository.updateAll(toUpsert); }); } return changes; @@ -191,9 +197,9 @@ class SyncService { DateTime since, ) getChangedAssets, ) async { - final currentUser = await _userRepository.me(); + final currentUser = _storeService.get(StoreKey.currentUser); final DateTime? since = - (await _eTagRepository.get(currentUser.isarId))?.time?.toUtc(); + (await _eTagRepository.get(currentUser.id))?.time?.toUtc(); if (since == null) return null; final DateTime now = DateTime.now(); final (toUpsert, toDelete) = await getChangedAssets(users, since); @@ -251,7 +257,7 @@ class SyncService { return false; } await _syncUsersFromServer(serverUsers); - final List users = await _userRepository.getAllAccessible(); + final List users = await _userRepository.getAll(); bool changes = false; for (User u in users) { changes |= await _syncRemoteAssetsForUser(u, loadAssets); @@ -269,7 +275,7 @@ class SyncService { return false; } final List inDb = await _assetRepository.getAll( - ownerId: user.isarId, + ownerId: user.id, sortBy: AssetSort.checksum, ); assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); @@ -296,12 +302,12 @@ class SyncService { } Future _updateUserAssetsETag(List users, DateTime time) { - final etags = users.map((u) => ETag(id: u.id, time: time)).toList(); + final etags = users.map((u) => ETag(id: u.uid, time: time)).toList(); return _eTagRepository.upsertAll(etags); } Future _clearUserAssetsETag(List users) { - final ids = users.map((u) => u.id).toList(); + final ids = users.map((u) => u.uid).toList(); return _eTagRepository.deleteByIds(ids); } @@ -373,9 +379,10 @@ class SyncService { ); // update shared users - final List sharedUsers = album.sharedUsers.toList(growable: false); + final List sharedUsers = + album.sharedUsers.map((u) => u.toDto()).toList(growable: false); sharedUsers.sort((a, b) => a.id.compareTo(b.id)); - final List users = dto.remoteUsers.toList() + final List users = dto.remoteUsers.map((u) => u.toDto()).toList() ..sort((a, b) => a.id.compareTo(b.id)); final List userIdsToAdd = []; final List usersToUnlink = []; @@ -384,7 +391,7 @@ class SyncService { sharedUsers, compare: (User a, User b) => a.id.compareTo(b.id), both: (a, b) => false, - onlyFirst: (User a) => userIdsToAdd.add(a.id), + onlyFirst: (User a) => userIdsToAdd.add(a.uid), onlySecond: (User a) => usersToUnlink.add(a), ); @@ -392,7 +399,7 @@ class SyncService { final (existingInDb, updated) = await _linkWithExistingFromDb(toAdd); await upsertAssetsWithExif(updated); final assetsToLink = existingInDb + updated; - final usersToLink = await _userRepository.getByIds(userIdsToAdd); + final usersToLink = await _userRepository.getByUserIds(userIdsToAdd); album.name = dto.name; album.shared = dto.shared; @@ -416,7 +423,7 @@ class SyncService { try { await _assetRepository.transaction(() async { await _assetRepository.updateAll(toUpdate); - await _albumRepository.addUsers(album, usersToLink); + await _albumRepository.addUsers(album, usersToLink.nonNulls.toList()); await _albumRepository.removeUsers(album, usersToUnlink); await _albumRepository.addAssets(album, assetsToLink); await _albumRepository.removeAssets(album, toUnlink); @@ -429,7 +436,7 @@ class SyncService { } if (album.shared || dto.shared) { - final userId = (await _userRepository.me()).isarId; + final userId = (_storeService.get(StoreKey.currentUser)).id; final foreign = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); existing.addAll(foreign); @@ -482,8 +489,7 @@ class SyncService { ); } else if (album.shared) { // delete assets in DB unless they belong to this user or are part of some other shared album or belong to a partner - final userIds = - (await _userRepository.getAllAccessible()).map((user) => user.isarId); + final userIds = (await _userRepository.getAll()).map((user) => user.id); final orphanedAssets = await _assetRepository.getByAlbum(album, notOwnedBy: userIds); deleteCandidates.addAll(orphanedAssets); @@ -566,7 +572,7 @@ class SyncService { // general case, e.g. some assets have been deleted or there are excluded albums on iOS final inDb = await _assetRepository.getByAlbum( dbAlbum, - ownerId: (await _userRepository.me()).isarId, + ownerId: (_storeService.get(StoreKey.currentUser)).id, sortBy: AssetSort.checksum, ); diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart index db85230662..03042e266b 100644 --- a/mobile/lib/services/timeline.service.dart +++ b/mobile/lib/services/timeline.service.dart @@ -1,41 +1,42 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/interfaces/timeline.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; import 'package:immich_mobile/repositories/timeline.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; final timelineServiceProvider = Provider((ref) { return TimelineService( ref.watch(timelineRepositoryProvider), - ref.watch(userRepositoryProvider), ref.watch(appSettingsServiceProvider), + ref.watch(storeServiceProvider), ); }); class TimelineService { final ITimelineRepository _timelineRepository; - final IUserRepository _userRepository; final AppSettingsService _appSettingsService; + final StoreService _storeService; const TimelineService( this._timelineRepository, - this._userRepository, this._appSettingsService, + this._storeService, ); Future> getTimelineUserIds() async { - final me = await _userRepository.me(); - return _timelineRepository.getTimelineUserIds(me.isarId); + final me = _storeService.get(StoreKey.currentUser); + return _timelineRepository.getTimelineUserIds(me.id); } Stream> watchTimelineUserIds() async* { - final me = await _userRepository.me(); - yield* _timelineRepository.watchTimelineUsers(me.isarId); + final me = _storeService.get(StoreKey.currentUser); + yield* _timelineRepository.watchTimelineUsers(me.id); } Stream watchHomeTimeline(int userId) { @@ -50,15 +51,15 @@ class TimelineService { } Stream watchArchiveTimeline() async* { - final user = await _userRepository.me(); + final user = _storeService.get(StoreKey.currentUser); - yield* _timelineRepository.watchArchiveTimeline(user.isarId); + yield* _timelineRepository.watchArchiveTimeline(user.id); } Stream watchFavoriteTimeline() async* { - final user = await _userRepository.me(); + final user = _storeService.get(StoreKey.currentUser); - yield* _timelineRepository.watchFavoriteTimeline(user.isarId); + yield* _timelineRepository.watchFavoriteTimeline(user.id); } Stream watchAlbumTimeline(Album album) async* { @@ -69,9 +70,9 @@ class TimelineService { } Stream watchTrashTimeline() async* { - final user = await _userRepository.me(); + final user = _storeService.get(StoreKey.currentUser); - yield* _timelineRepository.watchTrashTimeline(user.isarId); + yield* _timelineRepository.watchTrashTimeline(user.id); } Stream watchAllVideosTimeline() { @@ -96,9 +97,9 @@ class TimelineService { } Stream watchAssetSelectionTimeline() async* { - final user = await _userRepository.me(); + final user = _storeService.get(StoreKey.currentUser); - yield* _timelineRepository.watchAssetSelectionTimeline(user.isarId); + yield* _timelineRepository.watchAssetSelectionTimeline(user.id); } GroupAssetsBy _getGroupByOption() { diff --git a/mobile/lib/services/trash.service.dart b/mobile/lib/services/trash.service.dart index 8d6cdd8bab..338f063fd3 100644 --- a/mobile/lib/services/trash.service.dart +++ b/mobile/lib/services/trash.service.dart @@ -1,12 +1,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; - import 'package:immich_mobile/providers/api.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; - import 'package:immich_mobile/services/api.service.dart'; import 'package:openapi/api.dart'; @@ -14,16 +13,20 @@ final trashServiceProvider = Provider((ref) { return TrashService( ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider), - ref.watch(userRepositoryProvider), + ref.watch(storeServiceProvider), ); }); class TrashService { final ApiService _apiService; final IAssetRepository _assetRepository; - final IUserRepository _userRepository; + final StoreService _storeService; - TrashService(this._apiService, this._assetRepository, this._userRepository); + TrashService( + this._apiService, + this._assetRepository, + this._storeService, + ); Future restoreAssets(Iterable assetList) async { final remoteAssets = assetList.where((a) => a.isRemote); @@ -40,11 +43,11 @@ class TrashService { } Future emptyTrash() async { - final user = await _userRepository.me(); + final user = _storeService.get(StoreKey.currentUser); await _apiService.trashApi.emptyTrash(); - final trashedAssets = await _assetRepository.getTrashAssets(user.isarId); + final trashedAssets = await _assetRepository.getTrashAssets(user.id); final ids = trashedAssets.map((e) => e.remoteId!).toList(); await _assetRepository.transaction(() async { @@ -71,11 +74,11 @@ class TrashService { } Future restoreTrash() async { - final user = await _userRepository.me(); + final user = _storeService.get(StoreKey.currentUser); await _apiService.trashApi.restoreTrash(); - final trashedAssets = await _assetRepository.getTrashAssets(user.isarId); + final trashedAssets = await _assetRepository.getTrashAssets(user.id); final updatedAssets = trashedAssets.map((asset) { asset.isTrashed = false; return asset; diff --git a/mobile/lib/services/user.service.dart b/mobile/lib/services/user.service.dart index 921202ec59..38730b4775 100644 --- a/mobile/lib/services/user.service.dart +++ b/mobile/lib/services/user.service.dart @@ -1,12 +1,12 @@ import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/interfaces/partner_api.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; import 'package:immich_mobile/interfaces/user_api.interface.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/partner_api.repository.dart'; -import 'package:immich_mobile/repositories/user.repository.dart'; import 'package:immich_mobile/repositories/user_api.repository.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:logging/logging.dart'; @@ -31,10 +31,6 @@ class UserService { this._userRepository, ); - Future> getUsers({bool self = false}) { - return _userRepository.getAll(self: self); - } - Future<({String profileImagePath})?> uploadProfileImage(XFile image) async { try { return await _userApiRepository.createProfileImage( @@ -47,6 +43,10 @@ class UserService { } } + Future> getAll() async { + return await _userRepository.getAll(); + } + Future?> getUsersFromServer() async { List? users; try { @@ -65,36 +65,44 @@ class UserService { return null; } - users.sortBy((u) => u.id); - sharedBy.sortBy((u) => u.id); - sharedWith.sortBy((u) => u.id); + users.sortBy((u) => u.uid); + sharedBy.sortBy((u) => u.uid); + sharedWith.sortBy((u) => u.uid); + + final updatedSharedBy = []; diffSortedListsSync( users, sharedBy, compare: (User a, User b) => a.id.compareTo(b.id), - both: (User a, User b) => a.isPartnerSharedBy = true, - onlyFirst: (_) {}, - onlySecond: (_) {}, + both: (User a, User b) { + updatedSharedBy.add(a.copyWith(isPartnerSharedBy: true)); + return true; + }, + onlyFirst: (User a) => updatedSharedBy.add(a), + onlySecond: (User b) => updatedSharedBy.add(b), ); + final updatedSharedWith = []; + diffSortedListsSync( - users, + updatedSharedBy, sharedWith, compare: (User a, User b) => a.id.compareTo(b.id), both: (User a, User b) { - a.isPartnerSharedWith = true; - a.inTimeline = b.inTimeline; + updatedSharedWith.add( + a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true), + ); return true; }, - onlyFirst: (_) {}, - onlySecond: (_) {}, + onlyFirst: (User a) => updatedSharedWith.add(a), + onlySecond: (User b) => updatedSharedWith.add(b), ); - return users; + return updatedSharedWith; } Future clearTable() { - return _userRepository.clearTable(); + return _userRepository.deleteAll(); } } diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index db019798a3..21231becf6 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -10,10 +10,10 @@ import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:isar/isar.dart'; diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 990fc082d5..d2f0a2ac9d 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -5,8 +5,8 @@ import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:isar/isar.dart'; const int targetVersion = 8; diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart index ac62ecee03..ec984d1017 100644 --- a/mobile/lib/widgets/album/album_thumbnail_card.dart +++ b/mobile/lib/widgets/album/album_thumbnail_card.dart @@ -58,7 +58,7 @@ class AlbumThumbnailCard extends StatelessWidget { // Add the owner name to the subtitle String? owner; if (showOwner) { - if (album.ownerId == Store.get(StoreKey.currentUser).id) { + if (album.ownerId == Store.get(StoreKey.currentUser).uid) { owner = 'album_thumbnail_owned'.tr(); } else if (album.ownerName != null) { owner = 'album_thumbnail_shared_by'.tr(args: [album.ownerName!]); diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index 256141dc7d..1b225f106f 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -5,25 +5,25 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/pages/editing/edit.page.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; +import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_stack.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart'; -import 'package:immich_mobile/services/stack.service.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart'; -import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; -import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/widgets/common/immich_image.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/stack.service.dart'; +import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; +import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; +import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart'; +import 'package:immich_mobile/widgets/common/immich_image.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -import 'package:immich_mobile/pages/editing/edit.page.dart'; class BottomGalleryBar extends ConsumerWidget { final ValueNotifier assetIndex; @@ -49,7 +49,7 @@ class BottomGalleryBar extends ConsumerWidget { if (asset == null) { return const SizedBox(); } - final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.isarId; + final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.id; final showControls = ref.watch(showControlsProvider); final stackId = asset.stackId; diff --git a/mobile/lib/widgets/asset_viewer/description_input.dart b/mobile/lib/widgets/asset_viewer/description_input.dart index 2d9d71ac9e..844e4744b3 100644 --- a/mobile/lib/widgets/asset_viewer/description_input.dart +++ b/mobile/lib/widgets/asset_viewer/description_input.dart @@ -81,7 +81,7 @@ class DescriptionInput extends HookConsumerWidget { } return TextField( - enabled: owner?.isarId == asset.ownerId, + enabled: owner?.id == asset.ownerId, focusNode: focusNode, onTap: () => isFocus.value = true, onChanged: (value) { diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index d51e122954..4d4376b71d 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -67,8 +67,9 @@ class AppBarProfileInfoBox extends HookConsumerWidget { profileImagePath, ); if (user != null) { - user.profileImagePath = profileImagePath; - await Store.put(StoreKey.currentUser, user); + final updatedUser = + user.copyWith(profileImagePath: profileImagePath); + await Store.put(StoreKey.currentUser, updatedUser); ref.read(currentUserProvider.notifier).refresh(); } } diff --git a/mobile/lib/widgets/common/user_avatar.dart b/mobile/lib/widgets/common/user_avatar.dart index 62491210c9..b39aa6df17 100644 --- a/mobile/lib/widgets/common/user_avatar.dart +++ b/mobile/lib/widgets/common/user_avatar.dart @@ -1,8 +1,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/services/api.service.dart'; diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index 2b7eadf04b..4ece735bdf 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -4,8 +4,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/widgets/common/transparent_image.dart'; @@ -33,7 +33,7 @@ class UserCircleAvatar extends ConsumerWidget { style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12, - color: isDarkTheme && user.avatarColor == AvatarColorEnum.primary + color: isDarkTheme && user.avatarColor == AvatarColor.primary ? Colors.black : Colors.white, ), @@ -42,7 +42,7 @@ class UserCircleAvatar extends ConsumerWidget { return CircleAvatar( backgroundColor: user.avatarColor.toColor(), radius: radius, - child: user.profileImagePath.isEmpty + child: user.profileImagePath == null ? textIcon : ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(50)), diff --git a/mobile/test/fixtures/album.stub.dart b/mobile/test/fixtures/album.stub.dart index e820f193d5..c6ea199c0f 100644 --- a/mobile/test/fixtures/album.stub.dart +++ b/mobile/test/fixtures/album.stub.dart @@ -1,4 +1,5 @@ import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'asset.stub.dart'; import 'user.stub.dart'; @@ -26,7 +27,7 @@ final class AlbumStub { shared: true, activityEnabled: false, endDate: DateTime(2020), - )..sharedUsers.addAll([UserStub.admin]); + )..sharedUsers.addAll([User.fromDto(UserStub.admin)]); static final oneAsset = Album( name: "album-with-single-asset", @@ -53,7 +54,7 @@ final class AlbumStub { ) ..assets.addAll([AssetStub.image1, AssetStub.image2]) ..activityEnabled = true - ..owner.value = UserStub.admin; + ..owner.value = User.fromDto(UserStub.admin); static final create2020end2020Album = Album( name: "create2020update2020Album", diff --git a/mobile/test/fixtures/user.stub.dart b/mobile/test/fixtures/user.stub.dart index 38524f782c..b6cc2eb4bc 100644 --- a/mobile/test/fixtures/user.stub.dart +++ b/mobile/test/fixtures/user.stub.dart @@ -1,35 +1,35 @@ -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; -final class UserStub { +abstract final class UserStub { const UserStub._(); static final admin = User( - id: "admin", - updatedAt: DateTime(2021), + uid: "admin", email: "admin@test.com", name: "admin", - avatarColor: AvatarColorEnum.green, - profileImagePath: '', isAdmin: true, + updatedAt: DateTime(2021), + profileImagePath: null, + avatarColor: AvatarColor.primary, ); static final user1 = User( - id: "user1", - updatedAt: DateTime(2022), + uid: "user1", email: "user1@test.com", name: "user1", - avatarColor: AvatarColorEnum.red, - profileImagePath: '', isAdmin: false, + updatedAt: DateTime(2022), + profileImagePath: null, + avatarColor: AvatarColor.primary, ); static final user2 = User( - id: "user2", - updatedAt: DateTime(2023), + uid: "user2", email: "user2@test.com", name: "user2", - avatarColor: AvatarColorEnum.primary, - profileImagePath: '', isAdmin: false, + updatedAt: DateTime(2023), + profileImagePath: null, + avatarColor: AvatarColor.primary, ); } diff --git a/mobile/test/infrastructure/repositories/store_repository_test.dart b/mobile/test/infrastructure/repositories/store_repository_test.dart index 6fd3d3963a..442fc52a05 100644 --- a/mobile/test/infrastructure/repositories/store_repository_test.dart +++ b/mobile/test/infrastructure/repositories/store_repository_test.dart @@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:isar/isar.dart'; diff --git a/mobile/test/modules/activity/activities_page_test.dart b/mobile/test/modules/activity/activities_page_test.dart index 6b20692bcd..cf9238d205 100644 --- a/mobile/test/modules/activity/activities_page_test.dart +++ b/mobile/test/modules/activity/activities_page_test.dart @@ -9,7 +9,7 @@ import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/models/activities/activity.model.dart'; import 'package:immich_mobile/pages/common/activities.page.dart'; @@ -96,7 +96,7 @@ void main() { await db.writeTxn(() async { await db.clear(); // Save all assets - await db.users.put(UserStub.admin); + await db.users.put(User.fromDto(UserStub.admin)); await db.assets.putAll([AssetStub.image1, AssetStub.image2]); await db.albums.put(AlbumStub.twoAsset); await AlbumStub.twoAsset.owner.save(); diff --git a/mobile/test/modules/shared/shared_mocks.dart b/mobile/test/modules/shared/shared_mocks.dart index 013232da3e..2a89a36dd1 100644 --- a/mobile/test/modules/shared/shared_mocks.dart +++ b/mobile/test/modules/shared/shared_mocks.dart @@ -1,5 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:mocktail/mocktail.dart'; diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index a58de21613..094f7394a5 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -1,16 +1,16 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:mocktail/mocktail.dart'; @@ -56,8 +56,9 @@ void main() { final MockAlbumMediaRepository albumMediaRepository = MockAlbumMediaRepository(); final MockAlbumApiRepository albumApiRepository = MockAlbumApiRepository(); + final owner = User( - id: "1", + uid: "1", updatedAt: DateTime.now(), email: "a@b.c", name: "first last", @@ -93,20 +94,20 @@ void main() { assetRepository, exifInfoRepository, userRepository, + StoreService.I, eTagRepository, ); - when(() => eTagRepository.get(owner.isarId)) - .thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now())); + when(() => eTagRepository.get(owner.id)) + .thenAnswer((_) async => ETag(id: owner.uid, time: DateTime.now())); when(() => eTagRepository.deleteByIds(["1"])).thenAnswer((_) async {}); when(() => eTagRepository.upsertAll(any())).thenAnswer((_) async {}); - when(() => userRepository.me()).thenAnswer((_) async => owner); - when(() => userRepository.getAll(sortBy: UserSort.id)) - .thenAnswer((_) async => [owner]); - when(() => userRepository.getAllAccessible()) + // when(() => userRepository.me()).thenAnswer((_) async => owner); + when(() => userRepository.getAll(sortBy: SortUserBy.id)) .thenAnswer((_) async => [owner]); + when(() => userRepository.getAll()).thenAnswer((_) async => [owner]); when( () => assetRepository.getAll( - ownerId: owner.isarId, + ownerId: owner.id, sortBy: AssetSort.checksum, ), ).thenAnswer((_) async => initialAssets); @@ -180,7 +181,7 @@ void main() { expect(c1, isTrue); when( () => assetRepository.getAll( - ownerId: owner.isarId, + ownerId: owner.id, sortBy: AssetSort.checksum, ), ).thenAnswer((_) async => remoteAssets); @@ -194,7 +195,7 @@ void main() { final currentState = [...remoteAssets]; when( () => assetRepository.getAll( - ownerId: owner.isarId, + ownerId: owner.id, sortBy: AssetSort.checksum, ), ).thenAnswer((_) async => currentState); diff --git a/mobile/test/repository.mocks.dart b/mobile/test/repository.mocks.dart index 7443db2815..0dc81bbf17 100644 --- a/mobile/test/repository.mocks.dart +++ b/mobile/test/repository.mocks.dart @@ -1,4 +1,5 @@ import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; +import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/interfaces/album.interface.dart'; import 'package:immich_mobile/interfaces/album_api.interface.dart'; import 'package:immich_mobile/interfaces/album_media.interface.dart'; @@ -9,7 +10,6 @@ import 'package:immich_mobile/interfaces/auth_api.interface.dart'; import 'package:immich_mobile/interfaces/backup_album.interface.dart'; import 'package:immich_mobile/interfaces/etag.interface.dart'; import 'package:immich_mobile/interfaces/file_media.interface.dart'; -import 'package:immich_mobile/interfaces/user.interface.dart'; import 'package:mocktail/mocktail.dart'; class MockAlbumRepository extends Mock implements IAlbumRepository {} diff --git a/mobile/test/services/album.service_test.dart b/mobile/test/services/album.service_test.dart index 983b355dcb..5460faaa23 100644 --- a/mobile/test/services/album.service_test.dart +++ b/mobile/test/services/album.service_test.dart @@ -146,7 +146,7 @@ void main() { () => albumApiRepository.create( "name", assetIds: [AssetStub.image1.remoteId!], - sharedUserIds: [UserStub.user1.id], + sharedUserIds: [UserStub.user1.uid], ), ).called(1); verify( @@ -204,7 +204,7 @@ void main() { when( () => albumRepository.addUsers( AlbumStub.emptyAlbum, - AlbumStub.emptyAlbum.sharedUsers.toList(), + AlbumStub.emptyAlbum.sharedUsers.map((u) => u.toDto()).toList(), ), ).thenAnswer((_) async => AlbumStub.emptyAlbum); @@ -214,7 +214,7 @@ void main() { final result = await sut.addUsers( AlbumStub.emptyAlbum, - [UserStub.user2.id], + [UserStub.user2.uid], ); expect(result, true); diff --git a/mobile/test/services/entity.service_test.dart b/mobile/test/services/entity.service_test.dart index 8c8b49a7e0..4d2e4f75dc 100644 --- a/mobile/test/services/entity.service_test.dart +++ b/mobile/test/services/entity.service_test.dart @@ -1,7 +1,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/services/entity.service.dart'; import 'package:mocktail/mocktail.dart'; + import '../fixtures/asset.stub.dart'; import '../fixtures/user.stub.dart'; import '../repository.mocks.dart'; @@ -33,25 +35,32 @@ void main() { ) ..remoteThumbnailAssetId = AssetStub.image1.remoteId ..assets.addAll([AssetStub.image1, AssetStub.image1]) - ..owner.value = UserStub.user1 - ..sharedUsers.addAll([UserStub.admin, UserStub.admin]); + ..owner.value = User.fromDto(UserStub.user1) + ..sharedUsers.addAll( + [User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)], + ); - when(() => userRepository.get(album.ownerId!)) + when(() => userRepository.get(any())) + .thenAnswer((_) async => UserStub.admin); + when(() => userRepository.getByUserId(any())) .thenAnswer((_) async => UserStub.admin); when(() => assetRepository.getByRemoteId(AssetStub.image1.remoteId!)) .thenAnswer((_) async => AssetStub.image1); - when(() => userRepository.getByIds(any())) + when(() => userRepository.getByUserIds(any())) .thenAnswer((_) async => [UserStub.user1, UserStub.user2]); when(() => assetRepository.getAllByRemoteId(any())) .thenAnswer((_) async => [AssetStub.image1, AssetStub.image2]); await sut.fillAlbumWithDatabaseEntities(album); - expect(album.owner.value, UserStub.admin); + expect(album.owner.value?.toDto(), UserStub.admin); expect(album.thumbnail.value, AssetStub.image1); - expect(album.remoteUsers.toSet(), {UserStub.user1, UserStub.user2}); + expect( + album.remoteUsers.map((u) => u.toDto()).toSet(), + {UserStub.user1, UserStub.user2}, + ); expect(album.remoteAssets.toSet(), {AssetStub.image1, AssetStub.image2}); }); diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart index 19b2d6e705..a5a89a2440 100644 --- a/mobile/test/test_utils.dart +++ b/mobile/test/test_utils.dart @@ -12,10 +12,10 @@ import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:isar/isar.dart'; import 'package:mocktail/mocktail.dart';