drift(mobile): drift auth user sync

This commit is contained in:
wuzihao051119
2025-07-25 12:28:02 +08:00
parent ad65e9011a
commit 4677ceb03c
69 changed files with 9116 additions and 1206 deletions
@@ -4,8 +4,9 @@ import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart';
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
@@ -43,6 +44,7 @@ class IsarDatabaseRepository implements IDatabaseRepository {
@DriftDatabase(
tables: [
UserEntity,
AuthUserEntity,
UserMetadataEntity,
PartnerEntity,
LocalAlbumEntity,
@@ -74,7 +76,7 @@ class Drift extends $Drift implements IDatabaseRepository {
);
@override
int get schemaVersion => 4;
int get schemaVersion => 5;
@override
MigrationStrategy get migration => MigrationStrategy(
@@ -102,6 +104,12 @@ class Drift extends $Drift implements IDatabaseRepository {
// asset_face_entity is added
await m.create(v4.assetFaceEntity);
},
from4To5: (m, v5) async {
// Some column got moved from user_entity to auth_user_entity
await m.alterTable(TableMigration(v5.userEntity));
// auth_user_entity is added
await m.create(v5.authUserEntity);
},
),
);
@@ -13,29 +13,31 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.d
as i5;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
as i6;
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart'
as i7;
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
as i8;
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
as i9;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
as i10;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i11;
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
as i12;
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
as i13;
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
as i14;
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
as i15;
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'
as i16;
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'
as i17;
import 'package:drift/internal/modular.dart' as i18;
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
as i18;
import 'package:drift/internal/modular.dart' as i19;
abstract class $Drift extends i0.GeneratedDatabase {
$Drift(i0.QueryExecutor e) : super(e);
@@ -50,26 +52,28 @@ abstract class $Drift extends i0.GeneratedDatabase {
i5.$LocalAlbumEntityTable(this);
late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
i6.$LocalAlbumAssetEntityTable(this);
late final i7.$UserMetadataEntityTable userMetadataEntity =
i7.$UserMetadataEntityTable(this);
late final i8.$PartnerEntityTable partnerEntity =
i8.$PartnerEntityTable(this);
late final i9.$RemoteExifEntityTable remoteExifEntity =
i9.$RemoteExifEntityTable(this);
late final i10.$RemoteAlbumEntityTable remoteAlbumEntity =
i10.$RemoteAlbumEntityTable(this);
late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
i11.$RemoteAlbumAssetEntityTable(this);
late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity =
i12.$RemoteAlbumUserEntityTable(this);
late final i13.$MemoryEntityTable memoryEntity = i13.$MemoryEntityTable(this);
late final i14.$MemoryAssetEntityTable memoryAssetEntity =
i14.$MemoryAssetEntityTable(this);
late final i15.$PersonEntityTable personEntity = i15.$PersonEntityTable(this);
late final i16.$AssetFaceEntityTable assetFaceEntity =
i16.$AssetFaceEntityTable(this);
i17.MergedAssetDrift get mergedAssetDrift => i18.ReadDatabaseContainer(this)
.accessor<i17.MergedAssetDrift>(i17.MergedAssetDrift.new);
late final i7.$AuthUserEntityTable authUserEntity =
i7.$AuthUserEntityTable(this);
late final i8.$UserMetadataEntityTable userMetadataEntity =
i8.$UserMetadataEntityTable(this);
late final i9.$PartnerEntityTable partnerEntity =
i9.$PartnerEntityTable(this);
late final i10.$RemoteExifEntityTable remoteExifEntity =
i10.$RemoteExifEntityTable(this);
late final i11.$RemoteAlbumEntityTable remoteAlbumEntity =
i11.$RemoteAlbumEntityTable(this);
late final i12.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
i12.$RemoteAlbumAssetEntityTable(this);
late final i13.$RemoteAlbumUserEntityTable remoteAlbumUserEntity =
i13.$RemoteAlbumUserEntityTable(this);
late final i14.$MemoryEntityTable memoryEntity = i14.$MemoryEntityTable(this);
late final i15.$MemoryAssetEntityTable memoryAssetEntity =
i15.$MemoryAssetEntityTable(this);
late final i16.$PersonEntityTable personEntity = i16.$PersonEntityTable(this);
late final i17.$AssetFaceEntityTable assetFaceEntity =
i17.$AssetFaceEntityTable(this);
i18.MergedAssetDrift get mergedAssetDrift => i19.ReadDatabaseContainer(this)
.accessor<i18.MergedAssetDrift>(i18.MergedAssetDrift.new);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@@ -84,6 +88,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
i4.idxLocalAssetChecksum,
i2.uQRemoteAssetOwnerChecksum,
i2.idxRemoteAssetChecksum,
authUserEntity,
userMetadataEntity,
partnerEntity,
remoteExifEntity,
@@ -268,25 +273,27 @@ class $DriftManager {
i5.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
i7.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
i7.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
i8.$$PartnerEntityTableTableManager get partnerEntity =>
i8.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
i9.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
i10.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
i11.$$RemoteAlbumAssetEntityTableTableManager(
i7.$$AuthUserEntityTableTableManager get authUserEntity =>
i7.$$AuthUserEntityTableTableManager(_db, _db.authUserEntity);
i8.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
i8.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
i9.$$PartnerEntityTableTableManager get partnerEntity =>
i9.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
i10.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
i10.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
i11.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
i11.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
i12.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
i12.$$RemoteAlbumAssetEntityTableTableManager(
_db, _db.remoteAlbumAssetEntity);
i12.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i12
i13.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i13
.$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity);
i13.$$MemoryEntityTableTableManager get memoryEntity =>
i13.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
i14.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
i14.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
i15.$$PersonEntityTableTableManager get personEntity =>
i15.$$PersonEntityTableTableManager(_db, _db.personEntity);
i16.$$AssetFaceEntityTableTableManager get assetFaceEntity =>
i16.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity);
i14.$$MemoryEntityTableTableManager get memoryEntity =>
i14.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
i15.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
i15.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
i16.$$PersonEntityTableTableManager get personEntity =>
i16.$$PersonEntityTableTableManager(_db, _db.personEntity);
i17.$$AssetFaceEntityTableTableManager get assetFaceEntity =>
i17.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity);
}
File diff suppressed because it is too large Load Diff
@@ -7,7 +7,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftPartnerRepository(this._db) : super(_db);
Future<List<PartnerUserDto>> getPartners(String userId) {
Future<List<PartnerUser>> getPartners(String userId) {
final query = _db.select(_db.partnerEntity).join([
innerJoin(
_db.userEntity,
@@ -21,7 +21,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
return query.map((row) {
final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto(
return PartnerUser(
id: user.id,
email: user.email,
name: user.name,
@@ -31,11 +31,11 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
}
// Get users who we can share our library with
Future<List<PartnerUserDto>> getAvailablePartners(String currentUserId) {
Future<List<PartnerUser>> getAvailablePartners(String currentUserId) {
final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not());
return query.map((user) {
return PartnerUserDto(
return PartnerUser(
id: user.id,
email: user.email,
name: user.name,
@@ -45,7 +45,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
}
// Get users who are sharing their photos WITH the current user
Future<List<PartnerUserDto>> getSharedWith(String partnerId) {
Future<List<PartnerUser>> getSharedWith(String partnerId) {
final query = _db.select(_db.partnerEntity).join([
innerJoin(
_db.userEntity,
@@ -59,7 +59,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
return query.map((row) {
final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto(
return PartnerUser(
id: user.id,
email: user.email,
name: user.name,
@@ -69,7 +69,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
}
// Get users who the current user is sharing their photos TO
Future<List<PartnerUserDto>> getSharedBy(String userId) {
Future<List<PartnerUser>> getSharedBy(String userId) {
final query = _db.select(_db.partnerEntity).join([
innerJoin(
_db.userEntity,
@@ -83,7 +83,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
return query.map((row) {
final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto(
return PartnerUser(
id: user.id,
email: user.email,
name: user.name,
@@ -106,7 +106,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
return allPartnerIds;
}
Future<PartnerUserDto?> getPartner(String partnerId, String userId) {
Future<PartnerUser?> getPartner(String partnerId, String userId) {
final query = _db.select(_db.partnerEntity).join([
innerJoin(
_db.userEntity,
@@ -120,7 +120,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
return query.map((row) {
final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto(
return PartnerUser(
id: user.id,
email: user.email,
name: user.name,
@@ -129,7 +129,7 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
}).getSingleOrNull();
}
Future<bool> toggleShowInTimeline(PartnerUserDto partner, String userId) {
Future<bool> toggleShowInTimeline(PartnerUser partner, String userId) {
return _db.partnerEntity.update().replace(
PartnerEntityCompanion(
sharedById: Value(partner.id),
@@ -8,6 +8,7 @@ import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
enum SortRemoteAlbumsBy { id, updatedAt }
@@ -40,8 +41,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
]);
query
..where(_db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount])
..addColumns([_db.userEntity.name])
..addColumns([assetCount, _db.userEntity.name])
..groupBy([_db.remoteAlbumEntity.id]);
if (sortBy.isNotEmpty) {
@@ -147,34 +147,18 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
}).getSingle();
}
Future<List<UserDto>> getSharedUsers(String albumId) async {
final albumUserRows =
await (_db.select(_db.remoteAlbumUserEntity)..where((row) => row.albumId.equals(albumId))).get();
Future<List<User>> getSharedUsers(String albumId) async {
final query = _db.remoteAlbumUserEntity.selectOnly()
..join([
innerJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId),
useColumns: true,
),
])
..where(_db.remoteAlbumUserEntity.albumId.equals(albumId));
if (albumUserRows.isEmpty) {
return [];
}
final userIds = albumUserRows.map((row) => row.userId);
return (_db.select(_db.userEntity)..where((row) => row.id.isIn(userIds)))
.map(
(user) => UserDto(
id: user.id,
email: user.email,
name: user.name,
profileImagePath: user.profileImagePath?.isEmpty == true ? null : user.profileImagePath,
isAdmin: user.isAdmin,
updatedAt: user.updatedAt,
quotaSizeInBytes: user.quotaSizeInBytes ?? 0,
quotaUsageInBytes: user.quotaUsageInBytes,
memoryEnabled: true,
inTimeline: false,
isPartnerSharedBy: false,
isPartnerSharedWith: false,
),
)
.get();
return query.map((row) => row.readTable(_db.userEntity).toDto()).get();
}
Future<List<RemoteAsset>> getAssets(String albumId) {
@@ -41,6 +41,7 @@ class SyncApiRepository {
request.body = jsonEncode(
SyncStreamDto(
types: [
SyncRequestType.authUsersV1,
SyncRequestType.usersV1,
SyncRequestType.assetsV1,
SyncRequestType.assetExifsV1,
@@ -138,6 +139,7 @@ class SyncApiRepository {
}
const _kResponseMap = <SyncEntityType, Function(Object)>{
SyncEntityType.authUserV1: SyncAuthUserV1.fromJson,
SyncEntityType.userV1: SyncUserV1.fromJson,
SyncEntityType.userDeleteV1: SyncUserDeleteV1.fromJson,
SyncEntityType.partnerV1: SyncPartnerV1.fromJson,
@@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/domain/models/user_metadata.model.dart';
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart';
@@ -29,6 +30,38 @@ class SyncStreamRepository extends DriftDatabaseRepository {
SyncStreamRepository(super.db) : _db = db;
Future<void> updateAuthUsersV1(Iterable<SyncAuthUserV1> data) async {
try {
await _db.batch((batch) {
for (final authUser in data) {
final companion = AuthUserEntityCompanion(
name: Value(authUser.name),
email: Value(authUser.email),
deletedAt: Value(authUser.deletedAt),
avatarColor: Value(authUser.avatarColor?.toAvatarColor()),
isAdmin: Value(authUser.isAdmin),
oauthId: Value(authUser.oauthId),
pinCode: Value(authUser.pinCode),
hasProfileImage: Value(authUser.hasProfileImage),
profileChangedAt: Value(authUser.profileChangedAt),
quotaSizeInBytes: Value(authUser.quotaSizeInBytes),
quotaUsageInBytes: Value(authUser.quotaUsageInBytes),
storageLabel: Value(authUser.storageLabel),
);
batch.insert(
_db.authUserEntity,
companion.copyWith(id: Value(authUser.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: SyncAuthUserV1', error, stack);
rethrow;
}
}
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
try {
await _db.userEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.userId)));
@@ -45,6 +78,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
final companion = UserEntityCompanion(
name: Value(user.name),
email: Value(user.email),
deletedAt: Value(user.deletedAt),
avatarColor: Value(user.avatarColor?.toAvatarColor()),
);
batch.insert(
@@ -604,6 +639,22 @@ class SyncStreamRepository extends DriftDatabaseRepository {
}
}
extension on UserAvatarColor {
AvatarColor toAvatarColor() => switch (this) {
UserAvatarColor.amber => AvatarColor.amber,
UserAvatarColor.blue => AvatarColor.blue,
UserAvatarColor.gray => AvatarColor.gray,
UserAvatarColor.green => AvatarColor.green,
UserAvatarColor.orange => AvatarColor.orange,
UserAvatarColor.pink => AvatarColor.pink,
UserAvatarColor.primary => AvatarColor.primary,
UserAvatarColor.purple => AvatarColor.purple,
UserAvatarColor.red => AvatarColor.red,
UserAvatarColor.yellow => AvatarColor.yellow,
_ => throw Exception('Unknown AvatarColor value: $this'),
};
}
extension on AssetTypeEnum {
AssetType toAssetType() => switch (this) {
AssetTypeEnum.IMAGE => AssetType.image,
@@ -263,7 +263,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
required int offset,
required int count,
}) async {
final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull();
final albumData = await _db.managers.remoteAlbumEntity.filter((row) => row.id.equals(albumId)).getSingleOrNull();
// If album doesn't exist (was deleted), return empty list
if (albumData == null) {
@@ -10,18 +10,18 @@ class IsarUserRepository extends IsarDatabaseRepository {
Future<void> delete(List<String> ids) async {
await transaction(() async {
await _db.users.deleteAllById(ids);
await _db.isarUsers.deleteAllById(ids);
});
}
Future<void> deleteAll() async {
await transaction(() async {
await _db.users.clear();
await _db.isarUsers.clear();
});
}
Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
return (await _db.users
return (await _db.isarUsers
.where()
.optional(
sortBy != null,
@@ -35,31 +35,44 @@ class IsarUserRepository extends IsarDatabaseRepository {
}
Future<UserDto?> getByUserId(String id) async {
return (await _db.users.getById(id))?.toDto();
return (await _db.isarUsers.getById(id))?.toDto();
}
Future<List<UserDto?>> getByUserIds(List<String> ids) async {
return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
return (await _db.isarUsers.getAllById(ids)).map((u) => u?.toDto()).toList();
}
Future<bool> insert(UserDto user) async {
await transaction(() async {
await _db.users.put(entity.User.fromDto(user));
await _db.isarUsers.put(entity.IsarUser.fromDto(user));
});
return true;
}
Future<UserDto> update(UserDto user) async {
await transaction(() async {
await _db.users.put(entity.User.fromDto(user));
await _db.isarUsers.put(entity.IsarUser.fromDto(user));
});
return user;
}
Future<bool> updateAll(List<UserDto> users) async {
await transaction(() async {
await _db.users.putAll(users.map(entity.User.fromDto).toList());
await _db.isarUsers.putAll(users.map(entity.IsarUser.fromDto).toList());
});
return true;
}
}
class DriftUserRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftUserRepository(this._db) : super(_db);
Future<List<User>> getAll() {
return _db.managers.userEntity.orderBy((row) => row.id.asc()).map((row) => row.toDto()).get();
}
Future<User?> getById(String id) {
return _db.managers.userEntity.filter((row) => row.id.equals(id)).map((row) => row.toDto()).getSingleOrNull();
}
}