From 097e132fba660b9fdab50a14fb22002aafe5d551 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Wed, 30 Jul 2025 11:09:28 -0500 Subject: [PATCH] fix: user profile images not working in beta timeline (#20203) * fix user icons in album view * revert updateUsersV1 change * fix: UserDto merge issues * fix: update user entity * revert what I thought were merge issues turns out drift cant figure out when it needs to gen a file... * fix removed line * handle defaults for older servers * feat: checkpoint migrations * fix: use parenthesis instead of brackets * Update 1753800911775-ProfileImageCheckpointRemoval.ts * fix: sync stream updateUsersV1 --- .../drift_schemas/main/drift_schema_v5.json | Bin 0 -> 32635 bytes mobile/lib/domain/models/user.model.dart | 32 ++++++++---------- mobile/lib/domain/services/user.service.dart | 2 +- .../infrastructure/entities/user.entity.dart | 15 ++++---- .../entities/user.entity.drift.dart | Bin 26109 -> 23182 bytes .../repositories/db.repository.dart | 11 +++++- .../repositories/db.repository.steps.dart | Bin 60965 -> 71136 bytes .../repositories/remote_album.repository.dart | 5 ++- .../repositories/sync_stream.repository.dart | 7 +++- .../infrastructure/utils/user.converter.dart | 13 ++++--- .../pages/drift_user_selection.page.dart | 5 ++- mobile/lib/providers/auth.provider.dart | 1 - mobile/lib/utils/openapi_patching.dart | 5 +++ .../widgets/common/user_circle_avatar.dart | 11 +++--- mobile/openapi/lib/model/sync_user_v1.dart | Bin 3888 -> 4580 bytes .../domain/services/user_service_test.dart | 4 +-- mobile/test/drift/main/generated/schema.dart | Bin 802 -> 886 bytes .../test/drift/main/generated/schema_v5.dart | Bin 0 -> 206645 bytes mobile/test/fixtures/sync_stream.stub.dart | 20 +++++++++-- mobile/test/fixtures/user.stub.dart | 6 ++-- .../modules/shared/sync_service_test.dart | 9 ++++- open-api/immich-openapi-specs.json | 11 +++++- server/src/database.ts | 2 +- server/src/dtos/sync.dto.ts | 4 +-- server/src/queries/sync.repository.sql | 10 +++--- server/src/repositories/sync.repository.ts | 11 +----- ...800911775-ProfileImageCheckpointRemoval.ts | 25 ++++++++++++++ server/src/services/sync.service.ts | 4 +-- .../test/medium/specs/sync/sync-user.spec.ts | 2 ++ 29 files changed, 140 insertions(+), 75 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v5.json create mode 100644 mobile/test/drift/main/generated/schema_v5.dart create mode 100644 server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts diff --git a/mobile/drift_schemas/main/drift_schema_v5.json b/mobile/drift_schemas/main/drift_schema_v5.json new file mode 100644 index 0000000000000000000000000000000000000000..ce29eaabdc7026b03be073fed8ea22e1741681cc GIT binary patch literal 32635 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 SOA)dL(=~m}&@e{&`usoXNGIO_ literal 0 HcmV?d00001 diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index 1e83fa498..b0a66f7d7 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -11,7 +11,6 @@ class UserDto { final bool isAdmin; final DateTime updatedAt; - final String? profileImagePath; final AvatarColor avatarColor; final bool memoryEnabled; @@ -25,18 +24,22 @@ class UserDto { bool get hasQuota => quotaSizeInBytes > 0; + final bool hasProfileImage; + final DateTime profileChangedAt; + const UserDto({ required this.id, required this.email, required this.name, required this.isAdmin, required this.updatedAt, - this.profileImagePath, + required this.profileChangedAt, this.avatarColor = AvatarColor.primary, this.memoryEnabled = true, this.inTimeline = false, this.isPartnerSharedBy = false, this.isPartnerSharedWith = false, + this.hasProfileImage = false, this.quotaUsageInBytes = 0, this.quotaSizeInBytes = 0, }); @@ -49,14 +52,13 @@ email: $email, name: $name, isAdmin: $isAdmin, updatedAt: $updatedAt, -profileImagePath: ${profileImagePath ?? ''}, avatarColor: $avatarColor, memoryEnabled: $memoryEnabled, inTimeline: $inTimeline, isPartnerSharedBy: $isPartnerSharedBy, isPartnerSharedWith: $isPartnerSharedWith, -quotaUsageInBytes: $quotaUsageInBytes, -quotaSizeInBytes: $quotaSizeInBytes, +hasProfileImage: $hasProfileImage +profileChangedAt: $profileChangedAt }'''; } @@ -66,28 +68,26 @@ quotaSizeInBytes: $quotaSizeInBytes, String? name, bool? isAdmin, DateTime? updatedAt, - String? profileImagePath, AvatarColor? avatarColor, bool? memoryEnabled, bool? inTimeline, bool? isPartnerSharedBy, bool? isPartnerSharedWith, - int? quotaUsageInBytes, - int? quotaSizeInBytes, + bool? hasProfileImage, + DateTime? profileChangedAt, }) => UserDto( id: id ?? this.id, 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, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, ); @override @@ -101,12 +101,11 @@ quotaSizeInBytes: $quotaSizeInBytes, 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; + other.hasProfileImage == hasProfileImage && + other.profileChangedAt.isAtSameMomentAs(profileChangedAt); } @override @@ -116,14 +115,13 @@ quotaSizeInBytes: $quotaSizeInBytes, email.hashCode ^ updatedAt.hashCode ^ isAdmin.hashCode ^ - profileImagePath.hashCode ^ avatarColor.hashCode ^ memoryEnabled.hashCode ^ inTimeline.hashCode ^ isPartnerSharedBy.hashCode ^ isPartnerSharedWith.hashCode ^ - quotaUsageInBytes.hashCode ^ - quotaSizeInBytes.hashCode; + hasProfileImage.hashCode ^ + profileChangedAt.hashCode; } class PartnerUserDto { diff --git a/mobile/lib/domain/services/user.service.dart b/mobile/lib/domain/services/user.service.dart index 3e948fe0f..d347d8aa4 100644 --- a/mobile/lib/domain/services/user.service.dart +++ b/mobile/lib/domain/services/user.service.dart @@ -45,7 +45,7 @@ class UserService { Future createProfileImage(String name, Uint8List image) async { try { final path = await _userApiRepository.createProfileImage(name: name, data: image); - final updatedUser = getMyUser().copyWith(profileImagePath: path); + final updatedUser = getMyUser(); await _storeService.put(StoreKey.currentUser, updatedUser); await _isarUserRepository.update(updatedUser); return path; diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index ab5b9a562..78fc76b45 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -50,12 +50,10 @@ class User { isAdmin: dto.isAdmin, isPartnerSharedBy: dto.isPartnerSharedBy, isPartnerSharedWith: dto.isPartnerSharedWith, - profileImagePath: dto.profileImagePath ?? "", + profileImagePath: dto.hasProfileImage ? "HAS_PROFILE_IMAGE" : "", avatarColor: dto.avatarColor, memoryEnabled: dto.memoryEnabled, inTimeline: dto.inTimeline, - quotaUsageInBytes: dto.quotaUsageInBytes, - quotaSizeInBytes: dto.quotaSizeInBytes, ); UserDto toDto() => UserDto( @@ -64,12 +62,13 @@ class User { name: name, isAdmin: isAdmin, updatedAt: updatedAt, - profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, avatarColor: avatarColor, memoryEnabled: memoryEnabled, inTimeline: inTimeline, isPartnerSharedBy: isPartnerSharedBy, isPartnerSharedWith: isPartnerSharedWith, + hasProfileImage: profileImagePath.isNotEmpty, + profileChangedAt: updatedAt, quotaUsageInBytes: quotaUsageInBytes, quotaSizeInBytes: quotaSizeInBytes, ); @@ -82,11 +81,11 @@ class UserEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); TextColumn get email => text()(); - TextColumn get profileImagePath => text().nullable()(); + + BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))(); + DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - // Quota - IntColumn get quotaSizeInBytes => integer().nullable()(); - IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))(); @override Set get primaryKey => {id}; diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart index 2c3c8a1f9ca9198acbcef16143aa7756dbec2fc9..dbfddab4a03eff57c2acbe963216173900016ea2 100644 GIT binary patch delta 3324 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 diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 6e574afa8..353cabf31 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -66,7 +66,7 @@ class Drift extends $Drift implements IDatabaseRepository { : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override - int get schemaVersion => 4; + int get schemaVersion => 5; @override MigrationStrategy get migration => MigrationStrategy( @@ -94,6 +94,15 @@ class Drift extends $Drift implements IDatabaseRepository { // asset_face_entity is added await m.create(v4.assetFaceEntity); }, + from4To5: (m, v5) async { + await m.alterTable( + TableMigration( + v5.userEntity, + newColumns: [v5.userEntity.hasProfileImage, v5.userEntity.profileChangedAt], + columnTransformer: {v5.userEntity.profileChangedAt: currentDateAndTime}, + ), + ); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 5bf20780f4cad769dad32d0028f0c3e491c5af51..8129bba00c1230117307ffb8b8ea081d9c62b08e 100644 GIT binary patch delta 457 zcmZ2_hxx&3mJM58OfK9Z%xF564Fpx~#EIj$$3eL^n=Uimke0rfH z*QIJ#3wu4<>P_MI%g#2rB7xom!14Z zk%KiE%-d*h4^lF@pgeH0Nt)Q?2i1H)2VO9<6F@h1GGngnEwl)`jao%b53sP companion)); } diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index 19958beab..dc107e6fb 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -11,7 +11,8 @@ abstract final class UserConverter { name: dto.name, isAdmin: false, updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, + hasProfileImage: dto.profileImagePath.isNotEmpty, + profileChangedAt: dto.profileChangedAt, avatarColor: dto.avatarColor.toAvatarColor(), ); @@ -21,14 +22,13 @@ abstract final class UserConverter { 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, + profileChangedAt: adminDto.profileChangedAt, + hasProfileImage: adminDto.profileImagePath.isNotEmpty, ); static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( @@ -37,14 +37,13 @@ abstract final class UserConverter { 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, + profileChangedAt: dto.profileChangedAt, + hasProfileImage: dto.profileImagePath.isNotEmpty, ); } diff --git a/mobile/lib/presentation/pages/drift_user_selection.page.dart b/mobile/lib/presentation/pages/drift_user_selection.page.dart index e8835e714..5bd32aaf8 100644 --- a/mobile/lib/presentation/pages/drift_user_selection.page.dart +++ b/mobile/lib/presentation/pages/drift_user_selection.page.dart @@ -27,15 +27,14 @@ final driftUsersProvider = FutureProvider.autoDispose>((ref) async name: entity.name, email: entity.email, isAdmin: entity.isAdmin, - profileImagePath: entity.profileImagePath, updatedAt: entity.updatedAt, - quotaSizeInBytes: entity.quotaSizeInBytes ?? 0, - quotaUsageInBytes: entity.quotaUsageInBytes, isPartnerSharedBy: false, isPartnerSharedWith: false, avatarColor: AvatarColor.primary, memoryEnabled: true, inTimeline: true, + profileChangedAt: entity.profileChangedAt, + hasProfileImage: entity.hasProfileImage, ), ) .toList(); diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index fc3e08472..02f7920d6 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -167,7 +167,6 @@ class AuthNotifier extends StateNotifier { isAuthenticated: true, name: user.name, isAdmin: user.isAdmin, - profileImagePath: user.profileImagePath, ); return true; diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index efbcf1c13..3e4539019 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -40,6 +40,11 @@ dynamic upgradeDto(dynamic value, String targetType) { addDefault(value, 'isOnboarded', false); } break; + case 'SyncUserV1': + if (value is Map) { + addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String()); + addDefault(value, 'hasProfileImage', false); + } } } diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index 8be71e9b2..1fbfce6c5 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -32,6 +32,7 @@ class UserCircleAvatar extends ConsumerWidget { ), child: Text(user.name[0].toUpperCase()), ); + return Tooltip( message: user.name, child: Container( @@ -42,13 +43,12 @@ class UserCircleAvatar extends ConsumerWidget { child: CircleAvatar( backgroundColor: userAvatarColor, radius: radius, - child: user.profileImagePath == null - ? textIcon - : ClipRRect( + child: user.hasProfileImage + ? ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(50)), child: CachedNetworkImage( fit: BoxFit.cover, - cacheKey: user.profileImagePath, + cacheKey: user.profileChangedAt.toIso8601String(), width: size, height: size, placeholder: (_, __) => Image.memory(kTransparentImage), @@ -57,7 +57,8 @@ class UserCircleAvatar extends ConsumerWidget { fadeInDuration: const Duration(milliseconds: 300), errorWidget: (context, error, stackTrace) => textIcon, ), - ), + ) + : textIcon, ), ), ); diff --git a/mobile/openapi/lib/model/sync_user_v1.dart b/mobile/openapi/lib/model/sync_user_v1.dart index c01ddcc9fc0d636b45a6002270d042627ac9c8d2..b9fad5ae8c5fe09aff8cc8d9e90b837c37022213 100644 GIT binary patch delta 613 zcmdlW_e6QaRYv}d#NvRW{ItxRRL|VR^wh}*8I2|%WfTx7fCxHgB<7{3rZ|>Ne$N;x zl9ZpHqkyi%o=I8KC9xzmBr`Wv0Yh6X(={bD9SXL#7>1}a8yG35sc|U)L4HX_YLOng zNg!S5Vv|FeuVDB!K^tt628ISlmZ}Ie=h&*C3Fs(frr4@vrsyc-CFZ8us(?rx z47+Vr&<)>wmSq{E2AW~&u?n^dB^jB;dT0`pQ`lUL6%?|H^YfyM)X~jCXhWBo{E<~* z^9nX2CR;QEtrT(-3&Ij}N>lyQLMjVVZ9uVQrvb7+N1;d^P4(on9L0PJD88DU#~~$( jZk;+v?`BiZN30kD&Cf3_r=Wo8bRA&W*IIMca&Z9w$NSjA delta 92 zcmaE&yg_cmRmRCb8098gF$GRu!X!8O0n^3F51ExFo3dP)ti`G{IghnKC^N-YB{M}w wAulmE)m8;WZZ>0E#yDA+!*ug(4kMlmk^lez diff --git a/mobile/test/domain/services/user_service_test.dart b/mobile/test/domain/services/user_service_test.dart index b3d967154..395f38a20 100644 --- a/mobile/test/domain/services/user_service_test.dart +++ b/mobile/test/domain/services/user_service_test.dart @@ -98,7 +98,7 @@ void main() { group('createProfileImage', () { test('should return profile image path', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), @@ -115,7 +115,7 @@ void main() { test('should return null if profile image creation fails', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 22131b11bbfe0d3540b8a71275c7bd95a2f3257e..c42542afb3b289967b066b8e5a416baf7cbc262f 100644 GIT binary patch delta 94 zcmZ3)_Kj_W9;0k#Zb5!giGq4@az<)yVtkpYUP@w7iMm2!u|k>YW*x>#Mz!R`;#37w qD=q~nC`v6UEy@E*>$xPBBqjl69ZSMYC(mSR;m}bqjkV^g`W39Prxd2>f2QB~r diff --git a/mobile/test/drift/main/generated/schema_v5.dart b/mobile/test/drift/main/generated/schema_v5.dart new file mode 100644 index 0000000000000000000000000000000000000000..5c94ff26cbbc6114963689dee2fd23d264505d77 GIT binary patch literal 206645 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: Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index 57b953f12..fee77f35b 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -188,8 +188,8 @@ export class SyncService extends BaseService { const upsertType = SyncEntityType.UserV1; const upserts = this.syncRepository.user.getUpserts(checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data }); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } } diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 72661e119..c5d572d7d 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -35,9 +35,11 @@ describe(SyncEntityType.UserV1, () => { data: { deletedAt: user.deletedAt, email: user.email, + hasProfileImage: user.profileImagePath !== '', id: user.id, name: user.name, avatarColor: user.avatarColor, + profileChangedAt: user.profileChangedAt.toISOString(), }, type: 'UserV1', },