refactor domain models

This commit is contained in:
shenlong-tanwen 2025-04-22 00:29:26 +05:30
parent 5fc1a63810
commit e9f145a477
22 changed files with 270 additions and 356 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
abstract interface class IAlbumMediaRepository {

View File

@ -1,9 +1,9 @@
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
Future<void> insert(LocalAlbum localAlbum, Iterable<LocalAsset> assets);
Future<void> insert(LocalAlbum album, Iterable<LocalAsset> assets);
Future<void> addAssets(String albumId, Iterable<LocalAsset> assets);
@ -11,7 +11,7 @@ abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
Future<List<LocalAsset>> getAssetsForAlbum(String albumId);
Future<void> update(LocalAlbum localAlbum);
Future<void> update(LocalAlbum album);
Future<void> delete(String albumId);

View File

@ -1,5 +1,5 @@
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
abstract interface class ILocalAssetRepository implements IDatabaseRepository {
Future<LocalAsset> get(String assetId);

View File

@ -1,72 +1,46 @@
part 'local_asset.model.dart';
part 'merged_asset.model.dart';
part 'remote_asset.model.dart';
part of 'base_asset.model.dart';
enum AssetType {
// do not change this order!
other,
image,
video,
audio,
}
sealed class Asset {
final String name;
final String? checksum;
final AssetType type;
final DateTime createdAt;
final DateTime updatedAt;
final int? width;
final int? height;
final int? durationInSeconds;
class Asset extends BaseAsset {
final String id;
final String? localId;
const Asset({
required this.name,
required this.checksum,
required this.type,
required this.createdAt,
required this.updatedAt,
this.width,
this.height,
this.durationInSeconds,
required this.id,
this.localId,
required super.name,
required super.checksum,
required super.type,
required super.createdAt,
required super.updatedAt,
super.width,
super.height,
super.durationInSeconds,
super.isFavorite = false,
});
@override
String toString() {
return '''Asset {
name: $name,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}
}''';
id: $id,
name: $name,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"},
localId: ${localId ?? "<NA>"},
isFavorite: $isFavorite,
}''';
}
@override
bool operator ==(Object other) {
if (other is! Asset) return false;
if (identical(this, other)) return true;
if (other is Asset) {
return name == other.name &&
type == other.type &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
width == other.width &&
height == other.height &&
durationInSeconds == other.durationInSeconds;
}
return false;
return super == other && id == other.id && localId == other.localId;
}
@override
int get hashCode {
return name.hashCode ^
type.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
width.hashCode ^
height.hashCode ^
durationInSeconds.hashCode;
}
int get hashCode => super.hashCode ^ id.hashCode ^ localId.hashCode;
}

View File

@ -0,0 +1,76 @@
part 'asset.model.dart';
part 'local_asset.model.dart';
enum AssetType {
// do not change this order!
other,
image,
video,
audio,
}
sealed class BaseAsset {
final String name;
final String? checksum;
final AssetType type;
final DateTime createdAt;
final DateTime updatedAt;
final int? width;
final int? height;
final int? durationInSeconds;
final bool isFavorite;
const BaseAsset({
required this.name,
required this.checksum,
required this.type,
required this.createdAt,
required this.updatedAt,
this.width,
this.height,
this.durationInSeconds,
this.isFavorite = false,
});
@override
String toString() {
return '''BaseAsset {
name: $name,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"},
isFavorite: $isFavorite,
}''';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is BaseAsset) {
return name == other.name &&
type == other.type &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
width == other.width &&
height == other.height &&
durationInSeconds == other.durationInSeconds &&
isFavorite == other.isFavorite;
}
return false;
}
@override
int get hashCode {
return name.hashCode ^
type.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
width.hashCode ^
height.hashCode ^
durationInSeconds.hashCode ^
isFavorite.hashCode;
}
}

View File

@ -1,10 +1,12 @@
part of 'asset.model.dart';
part of 'base_asset.model.dart';
class LocalAsset extends Asset {
final String localId;
class LocalAsset extends BaseAsset {
final String id;
final String? remoteId;
const LocalAsset({
required this.localId,
required this.id,
this.remoteId,
required super.name,
super.checksum,
required super.type,
@ -13,19 +15,22 @@ class LocalAsset extends Asset {
super.width,
super.height,
super.durationInSeconds,
super.isFavorite = false,
});
@override
String toString() {
return '''LocalAsset {
localId: $localId,
id: $id,
name: $name,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}
durationInSeconds: ${durationInSeconds ?? "<NA>"},
remoteId: ${remoteId ?? "<NA>"}
isFavorite: $isFavorite,
}''';
}
@ -33,16 +38,15 @@ class LocalAsset extends Asset {
bool operator ==(Object other) {
if (other is! LocalAsset) return false;
if (identical(this, other)) return true;
return super == other && localId == other.localId;
return super == other && id == other.id && remoteId == other.remoteId;
}
@override
int get hashCode {
return super.hashCode ^ localId.hashCode;
}
int get hashCode => super.hashCode ^ id.hashCode ^ remoteId.hashCode;
LocalAsset copyWith({
String? localId,
String? id,
String? remoteId,
String? name,
String? checksum,
AssetType? type,
@ -51,9 +55,11 @@ class LocalAsset extends Asset {
int? width,
int? height,
int? durationInSeconds,
bool? isFavorite,
}) {
return LocalAsset(
localId: localId ?? this.localId,
id: id ?? this.id,
remoteId: remoteId ?? this.remoteId,
name: name ?? this.name,
checksum: checksum ?? this.checksum,
type: type ?? this.type,
@ -62,6 +68,7 @@ class LocalAsset extends Asset {
width: width ?? this.width,
height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
isFavorite: isFavorite ?? this.isFavorite,
);
}
}

View File

@ -1,48 +0,0 @@
part of 'asset.model.dart';
class MergedAsset extends Asset {
final String remoteId;
final String localId;
const MergedAsset({
required this.remoteId,
required this.localId,
required super.name,
required super.checksum,
required super.type,
required super.createdAt,
required super.updatedAt,
super.width,
super.height,
super.durationInSeconds,
});
@override
String toString() {
return '''MergedAsset {
remoteId: $remoteId,
localId: $localId,
name: $name,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}
}''';
}
@override
bool operator ==(Object other) {
if (other is! MergedAsset) return false;
if (identical(this, other)) return true;
return super == other &&
remoteId == other.remoteId &&
localId == other.localId;
}
@override
int get hashCode {
return super.hashCode ^ remoteId.hashCode ^ localId.hashCode;
}
}

View File

@ -1,43 +0,0 @@
part of 'asset.model.dart';
class RemoteAsset extends Asset {
final String remoteId;
const RemoteAsset({
required this.remoteId,
required super.name,
required super.checksum,
required super.type,
required super.createdAt,
required super.updatedAt,
super.width,
super.height,
super.durationInSeconds,
});
@override
String toString() {
return '''RemoteAsset {
remoteId: $remoteId,
name: $name,
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}
}''';
}
@override
bool operator ==(Object other) {
if (other is! RemoteAsset) return false;
if (identical(this, other)) return true;
return super == other && remoteId == other.remoteId;
}
@override
int get hashCode {
return super.hashCode ^ remoteId.hashCode;
}
}

View File

@ -11,8 +11,6 @@ class LocalAlbum {
final String name;
final DateTime updatedAt;
/// Whether the album contains all photos (i.e, the virtual "Recent" album)
final bool isAll;
final int assetCount;
final String? thumbnailId;
final BackupSelection backupSelection;
@ -24,7 +22,6 @@ class LocalAlbum {
this.assetCount = 0,
this.thumbnailId,
this.backupSelection = BackupSelection.none,
this.isAll = false,
});
LocalAlbum copyWith({
@ -34,7 +31,6 @@ class LocalAlbum {
int? assetCount,
NullableValue<String>? thumbnailId,
BackupSelection? backupSelection,
bool? isAll,
}) {
return LocalAlbum(
id: id ?? this.id,
@ -43,7 +39,6 @@ class LocalAlbum {
assetCount: assetCount ?? this.assetCount,
thumbnailId: thumbnailId?.getOrDefault(this.thumbnailId),
backupSelection: backupSelection ?? this.backupSelection,
isAll: isAll ?? this.isAll,
);
}
@ -56,7 +51,6 @@ class LocalAlbum {
other.name == name &&
other.updatedAt == updatedAt &&
other.assetCount == assetCount &&
other.isAll == isAll &&
other.thumbnailId == thumbnailId &&
other.backupSelection == backupSelection;
}
@ -67,7 +61,6 @@ class LocalAlbum {
name.hashCode ^
updatedAt.hashCode ^
assetCount.hashCode ^
isAll.hashCode ^
thumbnailId.hashCode ^
backupSelection.hashCode;
}
@ -81,7 +74,6 @@ updatedAt: $updatedAt,
assetCount: $assetCount,
thumbnailId: ${thumbnailId ?? '<NA>'},
backupSelection: $backupSelection,
isAll: $isAll
}''';
}
}

View File

@ -5,7 +5,7 @@ import 'package:flutter/widgets.dart';
import 'package:immich_mobile/domain/interfaces/album_media.interface.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
import 'package:immich_mobile/utils/diff.dart';
import 'package:immich_mobile/utils/nullable_value.dart';
@ -34,6 +34,7 @@ class DeviceSyncService {
// and not the albums.
final deviceAlbums =
(await _albumMediaRepository.getAll()).sortedBy((a) => a.id);
final dbAlbums =
await _localAlbumRepository.getAll(sortBy: SortLocalAlbumsBy.id);
@ -64,7 +65,7 @@ class DeviceSyncService {
final album = deviceAlbum.copyWith(
// The below assumes the list is already sorted by createdDate from the filter
thumbnailId: NullableValue.valueOrEmpty(assets.firstOrNull?.localId),
thumbnailId: NullableValue.valueOrEmpty(assets.firstOrNull?.id),
);
await _localAlbumRepository.insert(album, assets);
@ -100,7 +101,7 @@ class DeviceSyncService {
return false;
}
_log.fine("Device album ${dbAlbum.name} has changed. Syncing...");
_log.info("Device album ${dbAlbum.name} has changed. Syncing...");
// Faster path - only new assets added
if (await checkAddition(dbAlbum, deviceAlbum)) {
@ -157,13 +158,13 @@ class DeviceSyncService {
String? thumbnailId = dbAlbum.thumbnailId;
if (thumbnailId == null || newAssets.isNotEmpty) {
if (thumbnailId == null) {
thumbnailId = newAssets.firstOrNull?.localId;
thumbnailId = newAssets.firstOrNull?.id;
} else if (newAssets.isNotEmpty) {
// The below assumes the list is already sorted by createdDate from the filter
final oldThumbAsset = await _localAssetRepository.get(thumbnailId);
if (oldThumbAsset.createdAt
.isBefore(newAssets.firstOrNull!.createdAt)) {
thumbnailId = newAssets.firstOrNull?.localId;
thumbnailId = newAssets.firstOrNull?.id;
}
}
}
@ -205,14 +206,14 @@ class DeviceSyncService {
thumbnailId: const NullableValue.empty(),
backupSelection: dbAlbum.backupSelection,
),
assetIdsToDelete: assetsInDb.map((a) => a.localId),
assetIdsToDelete: assetsInDb.map((a) => a.id),
);
return true;
}
// The below assumes the list is already sorted by createdDate from the filter
String? thumbnailId = assetsInDevice.isNotEmpty
? assetsInDevice.firstOrNull?.localId
? assetsInDevice.firstOrNull?.id
: dbAlbum.thumbnailId;
final updatedDeviceAlbum = deviceAlbum.copyWith(
@ -228,8 +229,8 @@ class DeviceSyncService {
return true;
}
assert(assetsInDb.isSortedBy((a) => a.localId));
assetsInDevice.sort((a, b) => a.localId.compareTo(b.localId));
assert(assetsInDb.isSortedBy((a) => a.id));
assetsInDevice.sort((a, b) => a.id.compareTo(b.id));
final assetsToUpsert = <LocalAsset>[];
final assetsToDelete = <String>[];
@ -237,7 +238,7 @@ class DeviceSyncService {
diffSortedListsSync(
assetsInDb,
assetsInDevice,
compare: (a, b) => a.localId.compareTo(b.localId),
compare: (a, b) => a.id.compareTo(b.id),
both: (dbAsset, deviceAsset) {
// Custom comparison to check if the asset has been modified without
// comparing the checksum
@ -247,7 +248,7 @@ class DeviceSyncService {
}
return false;
},
onlyFirst: (dbAsset) => assetsToDelete.add(dbAsset.localId),
onlyFirst: (dbAsset) => assetsToDelete.add(dbAsset.id),
onlySecond: (deviceAsset) => assetsToUpsert.add(deviceAsset),
);

View File

@ -10,12 +10,10 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin {
TextColumn get id => text()();
TextColumn get name => text()();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get assetCount => integer().withDefault(const Constant(0))();
TextColumn get thumbnailId => text()
.nullable()
.references(LocalAssetEntity, #localId, onDelete: KeyAction.setNull)();
IntColumn get backupSelection => intEnum<BackupSelection>()();
BoolColumn get isAll => boolean().withDefault(const Constant(false))();
@override
Set<Column> get primaryKey => {id};
@ -30,7 +28,6 @@ extension LocalAlbumEntityX on LocalAlbumEntityData {
assetCount: assetCount,
thumbnailId: thumbnailId,
backupSelection: backupSelection,
isAll: isAll,
);
}
}

View File

@ -16,20 +16,16 @@ typedef $$LocalAlbumEntityTableCreateCompanionBuilder
required String id,
required String name,
i0.Value<DateTime> updatedAt,
i0.Value<int> assetCount,
i0.Value<String?> thumbnailId,
required i2.BackupSelection backupSelection,
i0.Value<bool> isAll,
});
typedef $$LocalAlbumEntityTableUpdateCompanionBuilder
= i1.LocalAlbumEntityCompanion Function({
i0.Value<String> id,
i0.Value<String> name,
i0.Value<DateTime> updatedAt,
i0.Value<int> assetCount,
i0.Value<String?> thumbnailId,
i0.Value<i2.BackupSelection> backupSelection,
i0.Value<bool> isAll,
});
final class $$LocalAlbumEntityTableReferences extends i0.BaseReferences<
@ -82,17 +78,11 @@ class $$LocalAlbumEntityTableFilterComposer
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get assetCount => $composableBuilder(
column: $table.assetCount, builder: (column) => i0.ColumnFilters(column));
i0.ColumnWithTypeConverterFilters<i2.BackupSelection, i2.BackupSelection, int>
get backupSelection => $composableBuilder(
column: $table.backupSelection,
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
i0.ColumnFilters<bool> get isAll => $composableBuilder(
column: $table.isAll, builder: (column) => i0.ColumnFilters(column));
i5.$$LocalAssetEntityTableFilterComposer get thumbnailId {
final i5.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
@ -135,17 +125,10 @@ class $$LocalAlbumEntityTableOrderingComposer
column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get assetCount => $composableBuilder(
column: $table.assetCount,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get backupSelection => $composableBuilder(
column: $table.backupSelection,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<bool> get isAll => $composableBuilder(
column: $table.isAll, builder: (column) => i0.ColumnOrderings(column));
i5.$$LocalAssetEntityTableOrderingComposer get thumbnailId {
final i5.$$LocalAssetEntityTableOrderingComposer composer =
$composerBuilder(
@ -189,16 +172,10 @@ class $$LocalAlbumEntityTableAnnotationComposer
i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumn<int> get assetCount => $composableBuilder(
column: $table.assetCount, builder: (column) => column);
i0.GeneratedColumnWithTypeConverter<i2.BackupSelection, int>
get backupSelection => $composableBuilder(
column: $table.backupSelection, builder: (column) => column);
i0.GeneratedColumn<bool> get isAll =>
$composableBuilder(column: $table.isAll, builder: (column) => column);
i5.$$LocalAssetEntityTableAnnotationComposer get thumbnailId {
final i5.$$LocalAssetEntityTableAnnotationComposer composer =
$composerBuilder(
@ -252,38 +229,30 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager<
i0.Value<String> id = const i0.Value.absent(),
i0.Value<String> name = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int> assetCount = const i0.Value.absent(),
i0.Value<String?> thumbnailId = const i0.Value.absent(),
i0.Value<i2.BackupSelection> backupSelection =
const i0.Value.absent(),
i0.Value<bool> isAll = const i0.Value.absent(),
}) =>
i1.LocalAlbumEntityCompanion(
id: id,
name: name,
updatedAt: updatedAt,
assetCount: assetCount,
thumbnailId: thumbnailId,
backupSelection: backupSelection,
isAll: isAll,
),
createCompanionCallback: ({
required String id,
required String name,
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int> assetCount = const i0.Value.absent(),
i0.Value<String?> thumbnailId = const i0.Value.absent(),
required i2.BackupSelection backupSelection,
i0.Value<bool> isAll = const i0.Value.absent(),
}) =>
i1.LocalAlbumEntityCompanion.insert(
id: id,
name: name,
updatedAt: updatedAt,
assetCount: assetCount,
thumbnailId: thumbnailId,
backupSelection: backupSelection,
isAll: isAll,
),
withReferenceMapper: (p0) => p0
.map((e) => (
@ -368,14 +337,6 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime);
static const i0.VerificationMeta _assetCountMeta =
const i0.VerificationMeta('assetCount');
@override
late final i0.GeneratedColumn<int> assetCount = i0.GeneratedColumn<int>(
'asset_count', aliasedName, false,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
defaultValue: const i4.Constant(0));
static const i0.VerificationMeta _thumbnailIdMeta =
const i0.VerificationMeta('thumbnailId');
@override
@ -392,19 +353,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
type: i0.DriftSqlType.int, requiredDuringInsert: true)
.withConverter<i2.BackupSelection>(
i1.$LocalAlbumEntityTable.$converterbackupSelection);
static const i0.VerificationMeta _isAllMeta =
const i0.VerificationMeta('isAll');
@override
late final i0.GeneratedColumn<bool> isAll = i0.GeneratedColumn<bool>(
'is_all', aliasedName, false,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints:
i0.GeneratedColumn.constraintIsAlways('CHECK ("is_all" IN (0, 1))'),
defaultValue: const i4.Constant(false));
@override
List<i0.GeneratedColumn> get $columns =>
[id, name, updatedAt, assetCount, thumbnailId, backupSelection, isAll];
[id, name, updatedAt, thumbnailId, backupSelection];
@override
String get aliasedName => _alias ?? actualTableName;
@override
@ -431,22 +382,12 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
context.handle(_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
}
if (data.containsKey('asset_count')) {
context.handle(
_assetCountMeta,
assetCount.isAcceptableOrUnknown(
data['asset_count']!, _assetCountMeta));
}
if (data.containsKey('thumbnail_id')) {
context.handle(
_thumbnailIdMeta,
thumbnailId.isAcceptableOrUnknown(
data['thumbnail_id']!, _thumbnailIdMeta));
}
if (data.containsKey('is_all')) {
context.handle(
_isAllMeta, isAll.isAcceptableOrUnknown(data['is_all']!, _isAllMeta));
}
return context;
}
@ -463,15 +404,11 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
assetCount: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}asset_count'])!,
thumbnailId: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_id']),
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
.fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int,
data['${effectivePrefix}backup_selection'])!),
isAll: attachedDatabase.typeMapping
.read(i0.DriftSqlType.bool, data['${effectivePrefix}is_all'])!,
);
}
@ -495,25 +432,20 @@ class LocalAlbumEntityData extends i0.DataClass
final String id;
final String name;
final DateTime updatedAt;
final int assetCount;
final String? thumbnailId;
final i2.BackupSelection backupSelection;
final bool isAll;
const LocalAlbumEntityData(
{required this.id,
required this.name,
required this.updatedAt,
required this.assetCount,
this.thumbnailId,
required this.backupSelection,
required this.isAll});
required this.backupSelection});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<String>(id);
map['name'] = i0.Variable<String>(name);
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
map['asset_count'] = i0.Variable<int>(assetCount);
if (!nullToAbsent || thumbnailId != null) {
map['thumbnail_id'] = i0.Variable<String>(thumbnailId);
}
@ -522,7 +454,6 @@ class LocalAlbumEntityData extends i0.DataClass
.$LocalAlbumEntityTable.$converterbackupSelection
.toSql(backupSelection));
}
map['is_all'] = i0.Variable<bool>(isAll);
return map;
}
@ -533,11 +464,9 @@ class LocalAlbumEntityData extends i0.DataClass
id: serializer.fromJson<String>(json['id']),
name: serializer.fromJson<String>(json['name']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
assetCount: serializer.fromJson<int>(json['assetCount']),
thumbnailId: serializer.fromJson<String?>(json['thumbnailId']),
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
.fromJson(serializer.fromJson<int>(json['backupSelection'])),
isAll: serializer.fromJson<bool>(json['isAll']),
);
}
@override
@ -547,12 +476,10 @@ class LocalAlbumEntityData extends i0.DataClass
'id': serializer.toJson<String>(id),
'name': serializer.toJson<String>(name),
'updatedAt': serializer.toJson<DateTime>(updatedAt),
'assetCount': serializer.toJson<int>(assetCount),
'thumbnailId': serializer.toJson<String?>(thumbnailId),
'backupSelection': serializer.toJson<int>(i1
.$LocalAlbumEntityTable.$converterbackupSelection
.toJson(backupSelection)),
'isAll': serializer.toJson<bool>(isAll),
};
}
@ -560,32 +487,25 @@ class LocalAlbumEntityData extends i0.DataClass
{String? id,
String? name,
DateTime? updatedAt,
int? assetCount,
i0.Value<String?> thumbnailId = const i0.Value.absent(),
i2.BackupSelection? backupSelection,
bool? isAll}) =>
i2.BackupSelection? backupSelection}) =>
i1.LocalAlbumEntityData(
id: id ?? this.id,
name: name ?? this.name,
updatedAt: updatedAt ?? this.updatedAt,
assetCount: assetCount ?? this.assetCount,
thumbnailId: thumbnailId.present ? thumbnailId.value : this.thumbnailId,
backupSelection: backupSelection ?? this.backupSelection,
isAll: isAll ?? this.isAll,
);
LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) {
return LocalAlbumEntityData(
id: data.id.present ? data.id.value : this.id,
name: data.name.present ? data.name.value : this.name,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
assetCount:
data.assetCount.present ? data.assetCount.value : this.assetCount,
thumbnailId:
data.thumbnailId.present ? data.thumbnailId.value : this.thumbnailId,
backupSelection: data.backupSelection.present
? data.backupSelection.value
: this.backupSelection,
isAll: data.isAll.present ? data.isAll.value : this.isAll,
);
}
@ -595,17 +515,15 @@ class LocalAlbumEntityData extends i0.DataClass
..write('id: $id, ')
..write('name: $name, ')
..write('updatedAt: $updatedAt, ')
..write('assetCount: $assetCount, ')
..write('thumbnailId: $thumbnailId, ')
..write('backupSelection: $backupSelection, ')
..write('isAll: $isAll')
..write('backupSelection: $backupSelection')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(
id, name, updatedAt, assetCount, thumbnailId, backupSelection, isAll);
int get hashCode =>
Object.hash(id, name, updatedAt, thumbnailId, backupSelection);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@ -613,10 +531,8 @@ class LocalAlbumEntityData extends i0.DataClass
other.id == this.id &&
other.name == this.name &&
other.updatedAt == this.updatedAt &&
other.assetCount == this.assetCount &&
other.thumbnailId == this.thumbnailId &&
other.backupSelection == this.backupSelection &&
other.isAll == this.isAll);
other.backupSelection == this.backupSelection);
}
class LocalAlbumEntityCompanion
@ -624,27 +540,21 @@ class LocalAlbumEntityCompanion
final i0.Value<String> id;
final i0.Value<String> name;
final i0.Value<DateTime> updatedAt;
final i0.Value<int> assetCount;
final i0.Value<String?> thumbnailId;
final i0.Value<i2.BackupSelection> backupSelection;
final i0.Value<bool> isAll;
const LocalAlbumEntityCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.assetCount = const i0.Value.absent(),
this.thumbnailId = const i0.Value.absent(),
this.backupSelection = const i0.Value.absent(),
this.isAll = const i0.Value.absent(),
});
LocalAlbumEntityCompanion.insert({
required String id,
required String name,
this.updatedAt = const i0.Value.absent(),
this.assetCount = const i0.Value.absent(),
this.thumbnailId = const i0.Value.absent(),
required i2.BackupSelection backupSelection,
this.isAll = const i0.Value.absent(),
}) : id = i0.Value(id),
name = i0.Value(name),
backupSelection = i0.Value(backupSelection);
@ -652,19 +562,15 @@ class LocalAlbumEntityCompanion
i0.Expression<String>? id,
i0.Expression<String>? name,
i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? assetCount,
i0.Expression<String>? thumbnailId,
i0.Expression<int>? backupSelection,
i0.Expression<bool>? isAll,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
if (updatedAt != null) 'updated_at': updatedAt,
if (assetCount != null) 'asset_count': assetCount,
if (thumbnailId != null) 'thumbnail_id': thumbnailId,
if (backupSelection != null) 'backup_selection': backupSelection,
if (isAll != null) 'is_all': isAll,
});
}
@ -672,18 +578,14 @@ class LocalAlbumEntityCompanion
{i0.Value<String>? id,
i0.Value<String>? name,
i0.Value<DateTime>? updatedAt,
i0.Value<int>? assetCount,
i0.Value<String?>? thumbnailId,
i0.Value<i2.BackupSelection>? backupSelection,
i0.Value<bool>? isAll}) {
i0.Value<i2.BackupSelection>? backupSelection}) {
return i1.LocalAlbumEntityCompanion(
id: id ?? this.id,
name: name ?? this.name,
updatedAt: updatedAt ?? this.updatedAt,
assetCount: assetCount ?? this.assetCount,
thumbnailId: thumbnailId ?? this.thumbnailId,
backupSelection: backupSelection ?? this.backupSelection,
isAll: isAll ?? this.isAll,
);
}
@ -699,9 +601,6 @@ class LocalAlbumEntityCompanion
if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
}
if (assetCount.present) {
map['asset_count'] = i0.Variable<int>(assetCount.value);
}
if (thumbnailId.present) {
map['thumbnail_id'] = i0.Variable<String>(thumbnailId.value);
}
@ -710,9 +609,6 @@ class LocalAlbumEntityCompanion
.$LocalAlbumEntityTable.$converterbackupSelection
.toSql(backupSelection.value));
}
if (isAll.present) {
map['is_all'] = i0.Variable<bool>(isAll.value);
}
return map;
}
@ -722,10 +618,8 @@ class LocalAlbumEntityCompanion
..write('id: $id, ')
..write('name: $name, ')
..write('updatedAt: $updatedAt, ')
..write('assetCount: $assetCount, ')
..write('thumbnailId: $thumbnailId, ')
..write('backupSelection: $backupSelection, ')
..write('isAll: $isAll')
..write('backupSelection: $backupSelection')
..write(')'))
.toString();
}

View File

@ -1,5 +1,5 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@ -12,6 +12,9 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
TextColumn get checksum => text().nullable()();
// Only used during backup to mirror the favorite status of the asset in the server
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
@override
Set<Column> get primaryKey => {localId};
}
@ -19,7 +22,7 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
extension LocalAssetEntityX on LocalAssetEntityData {
LocalAsset toDto() {
return LocalAsset(
localId: localId,
id: localId,
name: name,
checksum: checksum,
type: type,
@ -28,6 +31,7 @@ extension LocalAssetEntityX on LocalAssetEntityData {
width: width,
height: height,
durationInSeconds: durationInSeconds,
isFavorite: isFavorite,
);
}
}

View File

@ -3,7 +3,7 @@
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
as i1;
import 'package:immich_mobile/domain/models/asset/asset.model.dart' as i2;
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'
as i3;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
@ -19,6 +19,7 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder
i0.Value<int?> durationInSeconds,
required String localId,
i0.Value<String?> checksum,
i0.Value<bool> isFavorite,
});
typedef $$LocalAssetEntityTableUpdateCompanionBuilder
= i1.LocalAssetEntityCompanion Function({
@ -31,6 +32,7 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder
i0.Value<int?> durationInSeconds,
i0.Value<String> localId,
i0.Value<String?> checksum,
i0.Value<bool> isFavorite,
});
class $$LocalAssetEntityTableFilterComposer
@ -71,6 +73,9 @@ class $$LocalAssetEntityTableFilterComposer
i0.ColumnFilters<String> get checksum => $composableBuilder(
column: $table.checksum, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<bool> get isFavorite => $composableBuilder(
column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column));
}
class $$LocalAssetEntityTableOrderingComposer
@ -111,6 +116,10 @@ class $$LocalAssetEntityTableOrderingComposer
i0.ColumnOrderings<String> get checksum => $composableBuilder(
column: $table.checksum, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<bool> get isFavorite => $composableBuilder(
column: $table.isFavorite,
builder: (column) => i0.ColumnOrderings(column));
}
class $$LocalAssetEntityTableAnnotationComposer
@ -148,6 +157,9 @@ class $$LocalAssetEntityTableAnnotationComposer
i0.GeneratedColumn<String> get checksum =>
$composableBuilder(column: $table.checksum, builder: (column) => column);
i0.GeneratedColumn<bool> get isFavorite => $composableBuilder(
column: $table.isFavorite, builder: (column) => column);
}
class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
@ -188,6 +200,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
i0.Value<String> localId = const i0.Value.absent(),
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(),
}) =>
i1.LocalAssetEntityCompanion(
name: name,
@ -199,6 +212,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
durationInSeconds: durationInSeconds,
localId: localId,
checksum: checksum,
isFavorite: isFavorite,
),
createCompanionCallback: ({
required String name,
@ -210,6 +224,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
required String localId,
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(),
}) =>
i1.LocalAssetEntityCompanion.insert(
name: name,
@ -221,6 +236,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
durationInSeconds: durationInSeconds,
localId: localId,
checksum: checksum,
isFavorite: isFavorite,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
@ -312,6 +328,16 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
late final i0.GeneratedColumn<String> checksum = i0.GeneratedColumn<String>(
'checksum', aliasedName, true,
type: i0.DriftSqlType.string, requiredDuringInsert: false);
static const i0.VerificationMeta _isFavoriteMeta =
const i0.VerificationMeta('isFavorite');
@override
late final i0.GeneratedColumn<bool> isFavorite = i0.GeneratedColumn<bool>(
'is_favorite', aliasedName, false,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'CHECK ("is_favorite" IN (0, 1))'),
defaultValue: const i4.Constant(false));
@override
List<i0.GeneratedColumn> get $columns => [
name,
@ -322,7 +348,8 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
height,
durationInSeconds,
localId,
checksum
checksum,
isFavorite
];
@override
String get aliasedName => _alias ?? actualTableName;
@ -373,6 +400,12 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
context.handle(_checksumMeta,
checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta));
}
if (data.containsKey('is_favorite')) {
context.handle(
_isFavoriteMeta,
isFavorite.isAcceptableOrUnknown(
data['is_favorite']!, _isFavoriteMeta));
}
return context;
}
@ -402,6 +435,8 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
.read(i0.DriftSqlType.string, data['${effectivePrefix}local_id'])!,
checksum: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']),
isFavorite: attachedDatabase.typeMapping
.read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!,
);
}
@ -429,6 +464,7 @@ class LocalAssetEntityData extends i0.DataClass
final int? durationInSeconds;
final String localId;
final String? checksum;
final bool isFavorite;
const LocalAssetEntityData(
{required this.name,
required this.type,
@ -438,7 +474,8 @@ class LocalAssetEntityData extends i0.DataClass
this.height,
this.durationInSeconds,
required this.localId,
this.checksum});
this.checksum,
required this.isFavorite});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
@ -462,6 +499,7 @@ class LocalAssetEntityData extends i0.DataClass
if (!nullToAbsent || checksum != null) {
map['checksum'] = i0.Variable<String>(checksum);
}
map['is_favorite'] = i0.Variable<bool>(isFavorite);
return map;
}
@ -479,6 +517,7 @@ class LocalAssetEntityData extends i0.DataClass
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
localId: serializer.fromJson<String>(json['localId']),
checksum: serializer.fromJson<String?>(json['checksum']),
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
);
}
@override
@ -495,6 +534,7 @@ class LocalAssetEntityData extends i0.DataClass
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
'localId': serializer.toJson<String>(localId),
'checksum': serializer.toJson<String?>(checksum),
'isFavorite': serializer.toJson<bool>(isFavorite),
};
}
@ -507,7 +547,8 @@ class LocalAssetEntityData extends i0.DataClass
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
String? localId,
i0.Value<String?> checksum = const i0.Value.absent()}) =>
i0.Value<String?> checksum = const i0.Value.absent(),
bool? isFavorite}) =>
i1.LocalAssetEntityData(
name: name ?? this.name,
type: type ?? this.type,
@ -520,6 +561,7 @@ class LocalAssetEntityData extends i0.DataClass
: this.durationInSeconds,
localId: localId ?? this.localId,
checksum: checksum.present ? checksum.value : this.checksum,
isFavorite: isFavorite ?? this.isFavorite,
);
LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) {
return LocalAssetEntityData(
@ -534,6 +576,8 @@ class LocalAssetEntityData extends i0.DataClass
: this.durationInSeconds,
localId: data.localId.present ? data.localId.value : this.localId,
checksum: data.checksum.present ? data.checksum.value : this.checksum,
isFavorite:
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
);
}
@ -548,14 +592,15 @@ class LocalAssetEntityData extends i0.DataClass
..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ')
..write('localId: $localId, ')
..write('checksum: $checksum')
..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(name, type, createdAt, updatedAt, width,
height, durationInSeconds, localId, checksum);
height, durationInSeconds, localId, checksum, isFavorite);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@ -568,7 +613,8 @@ class LocalAssetEntityData extends i0.DataClass
other.height == this.height &&
other.durationInSeconds == this.durationInSeconds &&
other.localId == this.localId &&
other.checksum == this.checksum);
other.checksum == this.checksum &&
other.isFavorite == this.isFavorite);
}
class LocalAssetEntityCompanion
@ -582,6 +628,7 @@ class LocalAssetEntityCompanion
final i0.Value<int?> durationInSeconds;
final i0.Value<String> localId;
final i0.Value<String?> checksum;
final i0.Value<bool> isFavorite;
const LocalAssetEntityCompanion({
this.name = const i0.Value.absent(),
this.type = const i0.Value.absent(),
@ -592,6 +639,7 @@ class LocalAssetEntityCompanion
this.durationInSeconds = const i0.Value.absent(),
this.localId = const i0.Value.absent(),
this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(),
});
LocalAssetEntityCompanion.insert({
required String name,
@ -603,6 +651,7 @@ class LocalAssetEntityCompanion
this.durationInSeconds = const i0.Value.absent(),
required String localId,
this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(),
}) : name = i0.Value(name),
type = i0.Value(type),
localId = i0.Value(localId);
@ -616,6 +665,7 @@ class LocalAssetEntityCompanion
i0.Expression<int>? durationInSeconds,
i0.Expression<String>? localId,
i0.Expression<String>? checksum,
i0.Expression<bool>? isFavorite,
}) {
return i0.RawValuesInsertable({
if (name != null) 'name': name,
@ -627,6 +677,7 @@ class LocalAssetEntityCompanion
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
if (localId != null) 'local_id': localId,
if (checksum != null) 'checksum': checksum,
if (isFavorite != null) 'is_favorite': isFavorite,
});
}
@ -639,7 +690,8 @@ class LocalAssetEntityCompanion
i0.Value<int?>? height,
i0.Value<int?>? durationInSeconds,
i0.Value<String>? localId,
i0.Value<String?>? checksum}) {
i0.Value<String?>? checksum,
i0.Value<bool>? isFavorite}) {
return i1.LocalAssetEntityCompanion(
name: name ?? this.name,
type: type ?? this.type,
@ -650,6 +702,7 @@ class LocalAssetEntityCompanion
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
localId: localId ?? this.localId,
checksum: checksum ?? this.checksum,
isFavorite: isFavorite ?? this.isFavorite,
);
}
@ -684,6 +737,9 @@ class LocalAssetEntityCompanion
if (checksum.present) {
map['checksum'] = i0.Variable<String>(checksum.value);
}
if (isFavorite.present) {
map['is_favorite'] = i0.Variable<bool>(isFavorite.value);
}
return map;
}
@ -698,7 +754,8 @@ class LocalAssetEntityCompanion
..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ')
..write('localId: $localId, ')
..write('checksum: $checksum')
..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite')
..write(')'))
.toString();
}

View File

@ -1,11 +1,15 @@
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/interfaces/album_media.interface.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart' as asset;
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'
as asset;
import 'package:immich_mobile/domain/models/local_album.model.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:platform/platform.dart';
class AlbumMediaRepository implements IAlbumMediaRepository {
const AlbumMediaRepository();
final Platform _platform;
const AlbumMediaRepository({Platform platform = const LocalPlatform()})
: _platform = platform;
PMFilter _getAlbumFilter({
withAssetTitle = false,
@ -42,7 +46,12 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
);
return PhotoManager.getAssetPathList(hasAll: true, filterOption: filter)
.then((e) => e.toDtoList());
.then((e) {
if (_platform.isAndroid) {
e.removeWhere((a) => a.isAll);
}
return e.toDtoList();
});
}
@override
@ -85,7 +94,7 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
extension on AssetEntity {
Future<asset.LocalAsset> toDto() async => asset.LocalAsset(
localId: id,
id: id,
name: title ?? await titleAsync,
type: switch (type) {
AssetType.other => asset.AssetType.other,
@ -114,7 +123,6 @@ extension on AssetPathEntity {
// the assetCountAsync call is expensive for larger albums with several thousand assets
assetCount: withAssetCount ? await assetCountAsync : 0,
backupSelection: BackupSelection.none,
isAll: isAll,
);
}

View File

@ -1,6 +1,6 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
@ -137,10 +137,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
id: localAlbum.id,
name: localAlbum.name,
updatedAt: Value(localAlbum.updatedAt),
assetCount: Value(localAlbum.assetCount),
thumbnailId: Value.absentIfNull(localAlbum.thumbnailId),
backupSelection: localAlbum.backupSelection,
isAll: Value(localAlbum.isAll),
);
return _db.localAlbumEntity
@ -160,7 +158,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
_db.localAlbumAssetEntity,
assets.map(
(a) => LocalAlbumAssetEntityCompanion.insert(
assetId: a.localId,
assetId: a.id,
albumId: albumId,
),
),
@ -223,7 +221,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
width: Value.absentIfNull(a.width),
height: Value.absentIfNull(a.height),
durationInSeconds: Value.absentIfNull(a.durationInSeconds),
localId: a.localId,
localId: a.id,
checksum: Value.absentIfNull(a.checksum),
),
),

View File

@ -1,5 +1,5 @@
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';

View File

@ -1,5 +1,5 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
mixin AssetEntityMixin on Table {
TextColumn get name => text()();

View File

@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/domain/interfaces/album_media.interface.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
import 'package:immich_mobile/domain/services/device_sync.service.dart';
import 'package:immich_mobile/utils/nullable_value.dart';
@ -257,9 +257,9 @@ void main() {
newAlbum.copyWith(updatedAt: DateTime(2024), assetCount: 2);
final assets = [
LocalAssetStub.image1
.copyWith(localId: "asset1", createdAt: DateTime(2024, 1, 1)),
.copyWith(id: "asset1", createdAt: DateTime(2024, 1, 1)),
LocalAssetStub.image2.copyWith(
localId: "asset2",
id: "asset2",
createdAt: DateTime(2024, 1, 2),
),
];
@ -284,7 +284,7 @@ void main() {
expect(capturedAlbum.id, newAlbum.id);
expect(capturedAlbum.assetCount, refreshedAlbum.assetCount);
expect(capturedAlbum.updatedAt, refreshedAlbum.updatedAt);
expect(capturedAlbum.thumbnailId, assets.first.localId);
expect(capturedAlbum.thumbnailId, assets.first.id);
expect(listEquals(capturedAssets, assets), isTrue);
},
);
@ -354,7 +354,7 @@ void main() {
when(() => mockAlbumMediaRepo.refresh(dbAlbum.id))
.thenAnswer((_) async => refreshedAlbum);
final newAsset = LocalAssetStub.image2.copyWith(localId: "new_asset");
final newAsset = LocalAssetStub.image2.copyWith(id: "new_asset");
when(
() => mockAlbumMediaRepo.getAssetsForAlbum(
dbAlbum.id,
@ -387,7 +387,7 @@ void main() {
(a) =>
a.id == dbAlbum.id &&
a.assetCount == 2 &&
a.thumbnailId == newAsset.localId,
a.thumbnailId == newAsset.id,
),
),
),
@ -446,7 +446,7 @@ void main() {
).called(1);
verify(
() => mockLocalAlbumRepo
.removeAssets(dbAlbum.id, [LocalAssetStub.image1.localId]),
.removeAssets(dbAlbum.id, [LocalAssetStub.image1.id]),
).called(1);
},
);
@ -519,7 +519,7 @@ void main() {
test('returns true and updates assets/metadata on success', () async {
final newAsset = LocalAssetStub.image2.copyWith(
localId: "asset2",
id: "asset2",
createdAt: DateTime(2024, 1, 1, 10, 30, 0),
);
when(
@ -531,7 +531,7 @@ void main() {
when(() => mockLocalAssetRepo.get("thumb1")).thenAnswer(
(_) async => LocalAssetStub.image1.copyWith(
localId: "thumb1",
id: "thumb1",
createdAt: DateTime(2024, 1, 1, 9, 0, 0),
),
);
@ -556,7 +556,7 @@ void main() {
a.id == dbAlbum.id &&
a.assetCount == 2 &&
a.updatedAt == refreshedAlbum.updatedAt &&
a.thumbnailId == newAsset.localId,
a.thumbnailId == newAsset.id,
),
),
),
@ -567,7 +567,7 @@ void main() {
test('returns true and keeps old thumbnail if newer', () async {
final newAsset = LocalAssetStub.image2.copyWith(
localId: "asset2",
id: "asset2",
createdAt: DateTime(2024, 1, 1, 8, 0, 0),
);
when(
@ -579,7 +579,7 @@ void main() {
when(() => mockLocalAssetRepo.get("thumb1")).thenAnswer(
(_) async => LocalAssetStub.image1.copyWith(
localId: "thumb1",
id: "thumb1",
createdAt: DateTime(2024, 1, 1, 9, 0, 0),
),
);
@ -614,7 +614,7 @@ void main() {
test('returns true and sets new thumbnail if db thumb is null', () async {
final dbAlbumNoThumb = dbAlbum.copyWith(thumbnailId: null);
final newAsset = LocalAssetStub.image2.copyWith(
localId: "asset2",
id: "asset2",
createdAt: DateTime(2024, 1, 1, 10, 30, 0),
);
when(
@ -644,7 +644,7 @@ void main() {
a.id == dbAlbum.id &&
a.assetCount == 2 &&
a.updatedAt == refreshedAlbum.updatedAt &&
a.thumbnailId == newAsset.localId,
a.thumbnailId == newAsset.id,
),
),
),
@ -731,22 +731,22 @@ void main() {
);
final dbAsset1 = LocalAssetStub.image1.copyWith(
localId: "asset1",
id: "asset1",
createdAt: DateTime(2024),
updatedAt: DateTime(2024),
);
final dbAsset2 = LocalAssetStub.image2.copyWith(
localId: "asset2",
id: "asset2",
createdAt: DateTime(2024),
updatedAt: DateTime(2024),
); // To be deleted
final deviceAsset1 = LocalAssetStub.image1.copyWith(
localId: "asset1",
id: "asset1",
createdAt: DateTime(2024),
updatedAt: DateTime(2025),
); // Updated
final deviceAsset3 = LocalAssetStub.video1.copyWith(
localId: "asset3",
id: "asset3",
createdAt: DateTime(2024),
updatedAt: DateTime(2024),
); // Added
@ -819,7 +819,7 @@ void main() {
a.id == emptyDbAlbum.id &&
a.assetCount == deviceAssets.length &&
a.updatedAt == refreshedWithAssets.updatedAt &&
a.thumbnailId == deviceAssets.first.localId,
a.thumbnailId == deviceAssets.first.id,
),
),
),
@ -833,7 +833,7 @@ void main() {
final deviceAssets = [deviceAsset1, deviceAsset3];
deviceAssets.sort((a, b) => a.createdAt.compareTo(b.createdAt));
final dbAssets = [dbAsset1, dbAsset2];
dbAssets.sort((a, b) => a.localId.compareTo(b.localId));
dbAssets.sort((a, b) => a.id.compareTo(b.id));
when(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)).thenAnswer(
(_) async => deviceAssets,
@ -857,10 +857,10 @@ void main() {
return list.length == 2 &&
list.any(
(a) =>
a.localId == "asset1" &&
a.id == "asset1" &&
a.updatedAt == deviceAsset1.updatedAt,
) &&
list.any((a) => a.localId == "asset3");
list.any((a) => a.id == "asset3");
}),
),
),
@ -874,7 +874,7 @@ void main() {
a.id == dbAlbum.id &&
a.assetCount == 2 &&
a.updatedAt == currentRefreshedAlbum.updatedAt &&
a.thumbnailId == deviceAssets.first.localId,
a.thumbnailId == deviceAssets.first.id,
),
),
),
@ -892,7 +892,7 @@ void main() {
final dbAssets = [dbAsset1, dbAsset2];
final deviceAssets = [dbAsset1, dbAsset2];
deviceAssets.sort((a, b) => a.createdAt.compareTo(b.createdAt));
dbAssets.sort((a, b) => a.localId.compareTo(b.localId));
dbAssets.sort((a, b) => a.id.compareTo(b.id));
when(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id))
.thenAnswer((_) async => deviceAssets);
@ -915,7 +915,7 @@ void main() {
a.id == dbAlbum.id &&
a.assetCount == 2 &&
a.updatedAt == currentRefreshedAlbum.updatedAt &&
a.thumbnailId == deviceAssets.first.localId,
a.thumbnailId == deviceAssets.first.id,
),
),
),

View File

@ -10,7 +10,6 @@ abstract final class LocalAlbumStub {
assetCount: 1,
thumbnailId: null,
backupSelection: BackupSelection.none,
isAll: false,
);
static LocalAlbum get album2 => LocalAlbum(
@ -20,7 +19,6 @@ abstract final class LocalAlbumStub {
assetCount: 2,
thumbnailId: null,
backupSelection: BackupSelection.selected,
isAll: true,
);
static LocalAlbum get album3 => LocalAlbum(
@ -30,6 +28,5 @@ abstract final class LocalAlbumStub {
assetCount: 20,
thumbnailId: "123",
backupSelection: BackupSelection.excluded,
isAll: false,
);
}

View File

@ -1,10 +1,10 @@
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
abstract final class LocalAssetStub {
const LocalAssetStub();
static LocalAsset get image1 => LocalAsset(
localId: "image1",
id: "image1",
name: "image1.jpg",
checksum: "image1-checksum",
type: AssetType.image,
@ -16,7 +16,7 @@ abstract final class LocalAssetStub {
);
static LocalAsset get image2 => LocalAsset(
localId: "image2",
id: "image2",
name: "image2.jpg",
checksum: "image2-checksum",
type: AssetType.image,
@ -28,7 +28,7 @@ abstract final class LocalAssetStub {
);
static LocalAsset get video1 => LocalAsset(
localId: "video1",
id: "video1",
name: "video1.mov",
checksum: "video1-checksum",
type: AssetType.video,