feat: delta sync (#18428)

* feat: delta sync

* fix: ignore iCloud assets

* feat: dev logs

* add full sync button

* remove photo_manager dep for sync

* misc logs and fix

* add time taken to DLog

* fix: build release iOS

* ios sync go brrr

* rename local sync service

* update isar fork

* rename to platform assets / albums

* fix ci check

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
shenlong
2025-05-29 21:12:00 +05:30
committed by GitHub
parent 2b1b20ab0b
commit dbdb64f6c5
49 changed files with 5634 additions and 488 deletions

View File

@@ -0,0 +1,18 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class LocalAlbumEntity extends Table with DriftDefaultsMixin {
const LocalAlbumEntity();
TextColumn get id => text()();
TextColumn get name => text()();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get backupSelection => intEnum<BackupSelection>()();
// Used for mark & sweep
BoolColumn get marker_ => boolean().nullable()();
@override
Set<Column> get primaryKey => {id};
}

View File

@@ -0,0 +1,497 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
as i1;
import 'package:immich_mobile/domain/models/local_album.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'
as i3;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
typedef $$LocalAlbumEntityTableCreateCompanionBuilder
= i1.LocalAlbumEntityCompanion Function({
required String id,
required String name,
i0.Value<DateTime> updatedAt,
required i2.BackupSelection backupSelection,
i0.Value<bool?> marker_,
});
typedef $$LocalAlbumEntityTableUpdateCompanionBuilder
= i1.LocalAlbumEntityCompanion Function({
i0.Value<String> id,
i0.Value<String> name,
i0.Value<DateTime> updatedAt,
i0.Value<i2.BackupSelection> backupSelection,
i0.Value<bool?> marker_,
});
class $$LocalAlbumEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
$$LocalAlbumEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get id => $composableBuilder(
column: $table.id, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<String> get name => $composableBuilder(
column: $table.name, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt, 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 marker_ => $composableBuilder(
column: $table.marker_, builder: (column) => i0.ColumnFilters(column));
}
class $$LocalAlbumEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
$$LocalAlbumEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get id => $composableBuilder(
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<String> get name => $composableBuilder(
column: $table.name, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get backupSelection => $composableBuilder(
column: $table.backupSelection,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<bool> get marker_ => $composableBuilder(
column: $table.marker_, builder: (column) => i0.ColumnOrderings(column));
}
class $$LocalAlbumEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
$$LocalAlbumEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
i0.GeneratedColumn<String> get name =>
$composableBuilder(column: $table.name, builder: (column) => column);
i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumnWithTypeConverter<i2.BackupSelection, int>
get backupSelection => $composableBuilder(
column: $table.backupSelection, builder: (column) => column);
i0.GeneratedColumn<bool> get marker_ =>
$composableBuilder(column: $table.marker_, builder: (column) => column);
}
class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager<
i0.GeneratedDatabase,
i1.$LocalAlbumEntityTable,
i1.LocalAlbumEntityData,
i1.$$LocalAlbumEntityTableFilterComposer,
i1.$$LocalAlbumEntityTableOrderingComposer,
i1.$$LocalAlbumEntityTableAnnotationComposer,
$$LocalAlbumEntityTableCreateCompanionBuilder,
$$LocalAlbumEntityTableUpdateCompanionBuilder,
(
i1.LocalAlbumEntityData,
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable,
i1.LocalAlbumEntityData>
),
i1.LocalAlbumEntityData,
i0.PrefetchHooks Function()> {
$$LocalAlbumEntityTableTableManager(
i0.GeneratedDatabase db, i1.$LocalAlbumEntityTable table)
: super(i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$LocalAlbumEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () => i1
.$$LocalAlbumEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$LocalAlbumEntityTableAnnotationComposer(
$db: db, $table: table),
updateCompanionCallback: ({
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<i2.BackupSelection> backupSelection =
const i0.Value.absent(),
i0.Value<bool?> marker_ = const i0.Value.absent(),
}) =>
i1.LocalAlbumEntityCompanion(
id: id,
name: name,
updatedAt: updatedAt,
backupSelection: backupSelection,
marker_: marker_,
),
createCompanionCallback: ({
required String id,
required String name,
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
required i2.BackupSelection backupSelection,
i0.Value<bool?> marker_ = const i0.Value.absent(),
}) =>
i1.LocalAlbumEntityCompanion.insert(
id: id,
name: name,
updatedAt: updatedAt,
backupSelection: backupSelection,
marker_: marker_,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
));
}
typedef $$LocalAlbumEntityTableProcessedTableManager = i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$LocalAlbumEntityTable,
i1.LocalAlbumEntityData,
i1.$$LocalAlbumEntityTableFilterComposer,
i1.$$LocalAlbumEntityTableOrderingComposer,
i1.$$LocalAlbumEntityTableAnnotationComposer,
$$LocalAlbumEntityTableCreateCompanionBuilder,
$$LocalAlbumEntityTableUpdateCompanionBuilder,
(
i1.LocalAlbumEntityData,
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable,
i1.LocalAlbumEntityData>
),
i1.LocalAlbumEntityData,
i0.PrefetchHooks Function()>;
class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
with i0.TableInfo<$LocalAlbumEntityTable, i1.LocalAlbumEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$LocalAlbumEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
'id', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
static const i0.VerificationMeta _nameMeta =
const i0.VerificationMeta('name');
@override
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
static const i0.VerificationMeta _updatedAtMeta =
const i0.VerificationMeta('updatedAt');
@override
late final i0.GeneratedColumn<DateTime> updatedAt =
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime);
@override
late final i0.GeneratedColumnWithTypeConverter<i2.BackupSelection, int>
backupSelection = i0.GeneratedColumn<int>(
'backup_selection', aliasedName, false,
type: i0.DriftSqlType.int, requiredDuringInsert: true)
.withConverter<i2.BackupSelection>(
i1.$LocalAlbumEntityTable.$converterbackupSelection);
static const i0.VerificationMeta _marker_Meta =
const i0.VerificationMeta('marker_');
@override
late final i0.GeneratedColumn<bool> marker_ = i0.GeneratedColumn<bool>(
'marker', aliasedName, true,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints:
i0.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))'));
@override
List<i0.GeneratedColumn> get $columns =>
[id, name, updatedAt, backupSelection, marker_];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'local_album_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.LocalAlbumEntityData> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
if (data.containsKey('name')) {
context.handle(
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
} else if (isInserting) {
context.missing(_nameMeta);
}
if (data.containsKey('updated_at')) {
context.handle(_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
}
if (data.containsKey('marker')) {
context.handle(_marker_Meta,
marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta));
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.LocalAlbumEntityData map(Map<String, dynamic> data,
{String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.LocalAlbumEntityData(
id: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
name: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
.fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int,
data['${effectivePrefix}backup_selection'])!),
marker_: attachedDatabase.typeMapping
.read(i0.DriftSqlType.bool, data['${effectivePrefix}marker']),
);
}
@override
$LocalAlbumEntityTable createAlias(String alias) {
return $LocalAlbumEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.BackupSelection, int, int>
$converterbackupSelection =
const i0.EnumIndexConverter<i2.BackupSelection>(
i2.BackupSelection.values);
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class LocalAlbumEntityData extends i0.DataClass
implements i0.Insertable<i1.LocalAlbumEntityData> {
final String id;
final String name;
final DateTime updatedAt;
final i2.BackupSelection backupSelection;
final bool? marker_;
const LocalAlbumEntityData(
{required this.id,
required this.name,
required this.updatedAt,
required this.backupSelection,
this.marker_});
@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['backup_selection'] = i0.Variable<int>(i1
.$LocalAlbumEntityTable.$converterbackupSelection
.toSql(backupSelection));
}
if (!nullToAbsent || marker_ != null) {
map['marker'] = i0.Variable<bool>(marker_);
}
return map;
}
factory LocalAlbumEntityData.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return LocalAlbumEntityData(
id: serializer.fromJson<String>(json['id']),
name: serializer.fromJson<String>(json['name']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
.fromJson(serializer.fromJson<int>(json['backupSelection'])),
marker_: serializer.fromJson<bool?>(json['marker_']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<String>(id),
'name': serializer.toJson<String>(name),
'updatedAt': serializer.toJson<DateTime>(updatedAt),
'backupSelection': serializer.toJson<int>(i1
.$LocalAlbumEntityTable.$converterbackupSelection
.toJson(backupSelection)),
'marker_': serializer.toJson<bool?>(marker_),
};
}
i1.LocalAlbumEntityData copyWith(
{String? id,
String? name,
DateTime? updatedAt,
i2.BackupSelection? backupSelection,
i0.Value<bool?> marker_ = const i0.Value.absent()}) =>
i1.LocalAlbumEntityData(
id: id ?? this.id,
name: name ?? this.name,
updatedAt: updatedAt ?? this.updatedAt,
backupSelection: backupSelection ?? this.backupSelection,
marker_: marker_.present ? marker_.value : this.marker_,
);
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,
backupSelection: data.backupSelection.present
? data.backupSelection.value
: this.backupSelection,
marker_: data.marker_.present ? data.marker_.value : this.marker_,
);
}
@override
String toString() {
return (StringBuffer('LocalAlbumEntityData(')
..write('id: $id, ')
..write('name: $name, ')
..write('updatedAt: $updatedAt, ')
..write('backupSelection: $backupSelection, ')
..write('marker_: $marker_')
..write(')'))
.toString();
}
@override
int get hashCode =>
Object.hash(id, name, updatedAt, backupSelection, marker_);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.LocalAlbumEntityData &&
other.id == this.id &&
other.name == this.name &&
other.updatedAt == this.updatedAt &&
other.backupSelection == this.backupSelection &&
other.marker_ == this.marker_);
}
class LocalAlbumEntityCompanion
extends i0.UpdateCompanion<i1.LocalAlbumEntityData> {
final i0.Value<String> id;
final i0.Value<String> name;
final i0.Value<DateTime> updatedAt;
final i0.Value<i2.BackupSelection> backupSelection;
final i0.Value<bool?> marker_;
const LocalAlbumEntityCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.backupSelection = const i0.Value.absent(),
this.marker_ = const i0.Value.absent(),
});
LocalAlbumEntityCompanion.insert({
required String id,
required String name,
this.updatedAt = const i0.Value.absent(),
required i2.BackupSelection backupSelection,
this.marker_ = const i0.Value.absent(),
}) : id = i0.Value(id),
name = i0.Value(name),
backupSelection = i0.Value(backupSelection);
static i0.Insertable<i1.LocalAlbumEntityData> custom({
i0.Expression<String>? id,
i0.Expression<String>? name,
i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? backupSelection,
i0.Expression<bool>? marker_,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
if (updatedAt != null) 'updated_at': updatedAt,
if (backupSelection != null) 'backup_selection': backupSelection,
if (marker_ != null) 'marker': marker_,
});
}
i1.LocalAlbumEntityCompanion copyWith(
{i0.Value<String>? id,
i0.Value<String>? name,
i0.Value<DateTime>? updatedAt,
i0.Value<i2.BackupSelection>? backupSelection,
i0.Value<bool?>? marker_}) {
return i1.LocalAlbumEntityCompanion(
id: id ?? this.id,
name: name ?? this.name,
updatedAt: updatedAt ?? this.updatedAt,
backupSelection: backupSelection ?? this.backupSelection,
marker_: marker_ ?? this.marker_,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<String>(id.value);
}
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
}
if (backupSelection.present) {
map['backup_selection'] = i0.Variable<int>(i1
.$LocalAlbumEntityTable.$converterbackupSelection
.toSql(backupSelection.value));
}
if (marker_.present) {
map['marker'] = i0.Variable<bool>(marker_.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('LocalAlbumEntityCompanion(')
..write('id: $id, ')
..write('name: $name, ')
..write('updatedAt: $updatedAt, ')
..write('backupSelection: $backupSelection, ')
..write('marker_: $marker_')
..write(')'))
.toString();
}
}

View File

@@ -0,0 +1,17 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class LocalAlbumAssetEntity extends Table with DriftDefaultsMixin {
const LocalAlbumAssetEntity();
TextColumn get assetId =>
text().references(LocalAssetEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get albumId =>
text().references(LocalAlbumEntity, #id, onDelete: KeyAction.cascade)();
@override
Set<Column> get primaryKey => {assetId, albumId};
}

View File

@@ -0,0 +1,565 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'
as i2;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
as i3;
import 'package:drift/internal/modular.dart' as i4;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
as i5;
typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder
= i1.LocalAlbumAssetEntityCompanion Function({
required String assetId,
required String albumId,
});
typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder
= i1.LocalAlbumAssetEntityCompanion Function({
i0.Value<String> assetId,
i0.Value<String> albumId,
});
final class $$LocalAlbumAssetEntityTableReferences extends i0.BaseReferences<
i0.GeneratedDatabase,
i1.$LocalAlbumAssetEntityTable,
i1.LocalAlbumAssetEntityData> {
$$LocalAlbumAssetEntityTableReferences(
super.$_db, super.$_table, super.$_typedResult);
static i3.$LocalAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity')
.createAlias(i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$LocalAlbumAssetEntityTable>(
'local_album_asset_entity')
.assetId,
i4.ReadDatabaseContainer(db)
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity')
.id));
i3.$$LocalAssetEntityTableProcessedTableManager get assetId {
final $_column = $_itemColumn<String>('asset_id')!;
final manager = i3
.$$LocalAssetEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer($_db)
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'))
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
static i5.$LocalAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity')
.createAlias(i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$LocalAlbumAssetEntityTable>(
'local_album_asset_entity')
.albumId,
i4.ReadDatabaseContainer(db)
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity')
.id));
i5.$$LocalAlbumEntityTableProcessedTableManager get albumId {
final $_column = $_itemColumn<String>('album_id')!;
final manager = i5
.$$LocalAlbumEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer($_db)
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'))
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
}
class $$LocalAlbumAssetEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
$$LocalAlbumAssetEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$LocalAssetEntityTableFilterComposer get assetId {
final i3.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i3.$$LocalAssetEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i5.$$LocalAlbumEntityTableFilterComposer get albumId {
final i5.$$LocalAlbumEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$LocalAlbumEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$LocalAlbumAssetEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
$$LocalAlbumAssetEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$LocalAssetEntityTableOrderingComposer get assetId {
final i3.$$LocalAssetEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i3.$$LocalAssetEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i3.$LocalAssetEntityTable>(
'local_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i5.$$LocalAlbumEntityTableOrderingComposer get albumId {
final i5.$$LocalAlbumEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$LocalAlbumEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i5.$LocalAlbumEntityTable>(
'local_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$LocalAlbumAssetEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
$$LocalAlbumAssetEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$LocalAssetEntityTableAnnotationComposer get assetId {
final i3.$$LocalAssetEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i3.$$LocalAssetEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i3.$LocalAssetEntityTable>(
'local_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i5.$$LocalAlbumEntityTableAnnotationComposer get albumId {
final i5.$$LocalAlbumEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$LocalAlbumEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i5.$LocalAlbumEntityTable>(
'local_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager<
i0.GeneratedDatabase,
i1.$LocalAlbumAssetEntityTable,
i1.LocalAlbumAssetEntityData,
i1.$$LocalAlbumAssetEntityTableFilterComposer,
i1.$$LocalAlbumAssetEntityTableOrderingComposer,
i1.$$LocalAlbumAssetEntityTableAnnotationComposer,
$$LocalAlbumAssetEntityTableCreateCompanionBuilder,
$$LocalAlbumAssetEntityTableUpdateCompanionBuilder,
(i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences),
i1.LocalAlbumAssetEntityData,
i0.PrefetchHooks Function({bool assetId, bool albumId})> {
$$LocalAlbumAssetEntityTableTableManager(
i0.GeneratedDatabase db, i1.$LocalAlbumAssetEntityTable table)
: super(i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$LocalAlbumAssetEntityTableFilterComposer(
$db: db, $table: table),
createOrderingComposer: () =>
i1.$$LocalAlbumAssetEntityTableOrderingComposer(
$db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$LocalAlbumAssetEntityTableAnnotationComposer(
$db: db, $table: table),
updateCompanionCallback: ({
i0.Value<String> assetId = const i0.Value.absent(),
i0.Value<String> albumId = const i0.Value.absent(),
}) =>
i1.LocalAlbumAssetEntityCompanion(
assetId: assetId,
albumId: albumId,
),
createCompanionCallback: ({
required String assetId,
required String albumId,
}) =>
i1.LocalAlbumAssetEntityCompanion.insert(
assetId: assetId,
albumId: albumId,
),
withReferenceMapper: (p0) => p0
.map((e) => (
e.readTable(table),
i1.$$LocalAlbumAssetEntityTableReferences(db, table, e)
))
.toList(),
prefetchHooksCallback: ({assetId = false, albumId = false}) {
return i0.PrefetchHooks(
db: db,
explicitlyWatchedTables: [],
addJoins: <
T extends i0.TableManagerState<
dynamic,
dynamic,
dynamic,
dynamic,
dynamic,
dynamic,
dynamic,
dynamic,
dynamic,
dynamic,
dynamic>>(state) {
if (assetId) {
state = state.withJoin(
currentTable: table,
currentColumn: table.assetId,
referencedTable: i1.$$LocalAlbumAssetEntityTableReferences
._assetIdTable(db),
referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences
._assetIdTable(db)
.id,
) as T;
}
if (albumId) {
state = state.withJoin(
currentTable: table,
currentColumn: table.albumId,
referencedTable: i1.$$LocalAlbumAssetEntityTableReferences
._albumIdTable(db),
referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences
._albumIdTable(db)
.id,
) as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
));
}
typedef $$LocalAlbumAssetEntityTableProcessedTableManager
= i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$LocalAlbumAssetEntityTable,
i1.LocalAlbumAssetEntityData,
i1.$$LocalAlbumAssetEntityTableFilterComposer,
i1.$$LocalAlbumAssetEntityTableOrderingComposer,
i1.$$LocalAlbumAssetEntityTableAnnotationComposer,
$$LocalAlbumAssetEntityTableCreateCompanionBuilder,
$$LocalAlbumAssetEntityTableUpdateCompanionBuilder,
(
i1.LocalAlbumAssetEntityData,
i1.$$LocalAlbumAssetEntityTableReferences
),
i1.LocalAlbumAssetEntityData,
i0.PrefetchHooks Function({bool assetId, bool albumId})>;
class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity
with
i0
.TableInfo<$LocalAlbumAssetEntityTable, i1.LocalAlbumAssetEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$LocalAlbumAssetEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _assetIdMeta =
const i0.VerificationMeta('assetId');
@override
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
'asset_id', aliasedName, false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES local_asset_entity (id) ON DELETE CASCADE'));
static const i0.VerificationMeta _albumIdMeta =
const i0.VerificationMeta('albumId');
@override
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
'album_id', aliasedName, false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES local_album_entity (id) ON DELETE CASCADE'));
@override
List<i0.GeneratedColumn> get $columns => [assetId, albumId];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'local_album_asset_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.LocalAlbumAssetEntityData> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('asset_id')) {
context.handle(_assetIdMeta,
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta));
} else if (isInserting) {
context.missing(_assetIdMeta);
}
if (data.containsKey('album_id')) {
context.handle(_albumIdMeta,
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
} else if (isInserting) {
context.missing(_albumIdMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {assetId, albumId};
@override
i1.LocalAlbumAssetEntityData map(Map<String, dynamic> data,
{String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.LocalAlbumAssetEntityData(
assetId: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!,
albumId: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
);
}
@override
$LocalAlbumAssetEntityTable createAlias(String alias) {
return $LocalAlbumAssetEntityTable(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class LocalAlbumAssetEntityData extends i0.DataClass
implements i0.Insertable<i1.LocalAlbumAssetEntityData> {
final String assetId;
final String albumId;
const LocalAlbumAssetEntityData(
{required this.assetId, required this.albumId});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['asset_id'] = i0.Variable<String>(assetId);
map['album_id'] = i0.Variable<String>(albumId);
return map;
}
factory LocalAlbumAssetEntityData.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return LocalAlbumAssetEntityData(
assetId: serializer.fromJson<String>(json['assetId']),
albumId: serializer.fromJson<String>(json['albumId']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'assetId': serializer.toJson<String>(assetId),
'albumId': serializer.toJson<String>(albumId),
};
}
i1.LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) =>
i1.LocalAlbumAssetEntityData(
assetId: assetId ?? this.assetId,
albumId: albumId ?? this.albumId,
);
LocalAlbumAssetEntityData copyWithCompanion(
i1.LocalAlbumAssetEntityCompanion data) {
return LocalAlbumAssetEntityData(
assetId: data.assetId.present ? data.assetId.value : this.assetId,
albumId: data.albumId.present ? data.albumId.value : this.albumId,
);
}
@override
String toString() {
return (StringBuffer('LocalAlbumAssetEntityData(')
..write('assetId: $assetId, ')
..write('albumId: $albumId')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(assetId, albumId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.LocalAlbumAssetEntityData &&
other.assetId == this.assetId &&
other.albumId == this.albumId);
}
class LocalAlbumAssetEntityCompanion
extends i0.UpdateCompanion<i1.LocalAlbumAssetEntityData> {
final i0.Value<String> assetId;
final i0.Value<String> albumId;
const LocalAlbumAssetEntityCompanion({
this.assetId = const i0.Value.absent(),
this.albumId = const i0.Value.absent(),
});
LocalAlbumAssetEntityCompanion.insert({
required String assetId,
required String albumId,
}) : assetId = i0.Value(assetId),
albumId = i0.Value(albumId);
static i0.Insertable<i1.LocalAlbumAssetEntityData> custom({
i0.Expression<String>? assetId,
i0.Expression<String>? albumId,
}) {
return i0.RawValuesInsertable({
if (assetId != null) 'asset_id': assetId,
if (albumId != null) 'album_id': albumId,
});
}
i1.LocalAlbumAssetEntityCompanion copyWith(
{i0.Value<String>? assetId, i0.Value<String>? albumId}) {
return i1.LocalAlbumAssetEntityCompanion(
assetId: assetId ?? this.assetId,
albumId: albumId ?? this.albumId,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (assetId.present) {
map['asset_id'] = i0.Variable<String>(assetId.value);
}
if (albumId.present) {
map['album_id'] = i0.Variable<String>(albumId.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('LocalAlbumAssetEntityCompanion(')
..write('assetId: $assetId, ')
..write('albumId: $albumId')
..write(')'))
.toString();
}
}

View File

@@ -0,0 +1,17 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@TableIndex(name: 'local_asset_checksum', columns: {#checksum})
class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
const LocalAssetEntity();
TextColumn get id => text()();
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 => {id};
}

View File

@@ -0,0 +1,658 @@
// dart format width=80
// ignore_for_file: type=lint
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/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;
typedef $$LocalAssetEntityTableCreateCompanionBuilder
= i1.LocalAssetEntityCompanion Function({
required String name,
required i2.AssetType type,
i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt,
i0.Value<int?> durationInSeconds,
required String id,
i0.Value<String?> checksum,
i0.Value<bool> isFavorite,
});
typedef $$LocalAssetEntityTableUpdateCompanionBuilder
= i1.LocalAssetEntityCompanion Function({
i0.Value<String> name,
i0.Value<i2.AssetType> type,
i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt,
i0.Value<int?> durationInSeconds,
i0.Value<String> id,
i0.Value<String?> checksum,
i0.Value<bool> isFavorite,
});
class $$LocalAssetEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAssetEntityTable> {
$$LocalAssetEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get name => $composableBuilder(
column: $table.name, builder: (column) => i0.ColumnFilters(column));
i0.ColumnWithTypeConverterFilters<i2.AssetType, i2.AssetType, int> get type =>
$composableBuilder(
column: $table.type,
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds,
builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<String> get id => $composableBuilder(
column: $table.id, builder: (column) => i0.ColumnFilters(column));
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
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAssetEntityTable> {
$$LocalAssetEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get name => $composableBuilder(
column: $table.name, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get type => $composableBuilder(
column: $table.type, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<String> get id => $composableBuilder(
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
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
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAssetEntityTable> {
$$LocalAssetEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get name =>
$composableBuilder(column: $table.name, builder: (column) => column);
i0.GeneratedColumnWithTypeConverter<i2.AssetType, int> get type =>
$composableBuilder(column: $table.type, builder: (column) => column);
i0.GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds, builder: (column) => column);
i0.GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
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<
i0.GeneratedDatabase,
i1.$LocalAssetEntityTable,
i1.LocalAssetEntityData,
i1.$$LocalAssetEntityTableFilterComposer,
i1.$$LocalAssetEntityTableOrderingComposer,
i1.$$LocalAssetEntityTableAnnotationComposer,
$$LocalAssetEntityTableCreateCompanionBuilder,
$$LocalAssetEntityTableUpdateCompanionBuilder,
(
i1.LocalAssetEntityData,
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAssetEntityTable,
i1.LocalAssetEntityData>
),
i1.LocalAssetEntityData,
i0.PrefetchHooks Function()> {
$$LocalAssetEntityTableTableManager(
i0.GeneratedDatabase db, i1.$LocalAssetEntityTable table)
: super(i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$LocalAssetEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () => i1
.$$LocalAssetEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$LocalAssetEntityTableAnnotationComposer(
$db: db, $table: table),
updateCompanionCallback: ({
i0.Value<String> name = const i0.Value.absent(),
i0.Value<i2.AssetType> type = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
i0.Value<String> id = const i0.Value.absent(),
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(),
}) =>
i1.LocalAssetEntityCompanion(
name: name,
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
durationInSeconds: durationInSeconds,
id: id,
checksum: checksum,
isFavorite: isFavorite,
),
createCompanionCallback: ({
required String name,
required i2.AssetType type,
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
required String id,
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(),
}) =>
i1.LocalAssetEntityCompanion.insert(
name: name,
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
durationInSeconds: durationInSeconds,
id: id,
checksum: checksum,
isFavorite: isFavorite,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
));
}
typedef $$LocalAssetEntityTableProcessedTableManager = i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$LocalAssetEntityTable,
i1.LocalAssetEntityData,
i1.$$LocalAssetEntityTableFilterComposer,
i1.$$LocalAssetEntityTableOrderingComposer,
i1.$$LocalAssetEntityTableAnnotationComposer,
$$LocalAssetEntityTableCreateCompanionBuilder,
$$LocalAssetEntityTableUpdateCompanionBuilder,
(
i1.LocalAssetEntityData,
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAssetEntityTable,
i1.LocalAssetEntityData>
),
i1.LocalAssetEntityData,
i0.PrefetchHooks Function()>;
i0.Index get localAssetChecksum => i0.Index('local_asset_checksum',
'CREATE INDEX local_asset_checksum ON local_asset_entity (checksum)');
class $LocalAssetEntityTable extends i3.LocalAssetEntity
with i0.TableInfo<$LocalAssetEntityTable, i1.LocalAssetEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$LocalAssetEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _nameMeta =
const i0.VerificationMeta('name');
@override
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
@override
late final i0.GeneratedColumnWithTypeConverter<i2.AssetType, int> type =
i0.GeneratedColumn<int>('type', aliasedName, false,
type: i0.DriftSqlType.int, requiredDuringInsert: true)
.withConverter<i2.AssetType>(
i1.$LocalAssetEntityTable.$convertertype);
static const i0.VerificationMeta _createdAtMeta =
const i0.VerificationMeta('createdAt');
@override
late final i0.GeneratedColumn<DateTime> createdAt =
i0.GeneratedColumn<DateTime>('created_at', aliasedName, false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime);
static const i0.VerificationMeta _updatedAtMeta =
const i0.VerificationMeta('updatedAt');
@override
late final i0.GeneratedColumn<DateTime> updatedAt =
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime);
static const i0.VerificationMeta _durationInSecondsMeta =
const i0.VerificationMeta('durationInSeconds');
@override
late final i0.GeneratedColumn<int> durationInSeconds =
i0.GeneratedColumn<int>('duration_in_seconds', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
'id', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
static const i0.VerificationMeta _checksumMeta =
const i0.VerificationMeta('checksum');
@override
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,
type,
createdAt,
updatedAt,
durationInSeconds,
id,
checksum,
isFavorite
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'local_asset_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.LocalAssetEntityData> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('name')) {
context.handle(
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
} else if (isInserting) {
context.missing(_nameMeta);
}
if (data.containsKey('created_at')) {
context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
}
if (data.containsKey('updated_at')) {
context.handle(_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
}
if (data.containsKey('duration_in_seconds')) {
context.handle(
_durationInSecondsMeta,
durationInSeconds.isAcceptableOrUnknown(
data['duration_in_seconds']!, _durationInSecondsMeta));
}
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
if (data.containsKey('checksum')) {
context.handle(_checksumMeta,
checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta));
}
if (data.containsKey('is_favorite')) {
context.handle(
_isFavoriteMeta,
isFavorite.isAcceptableOrUnknown(
data['is_favorite']!, _isFavoriteMeta));
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.LocalAssetEntityData map(Map<String, dynamic> data,
{String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.LocalAssetEntityData(
name: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
type: i1.$LocalAssetEntityTable.$convertertype.fromSql(attachedDatabase
.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!),
createdAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
durationInSeconds: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
id: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
checksum: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']),
isFavorite: attachedDatabase.typeMapping
.read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!,
);
}
@override
$LocalAssetEntityTable createAlias(String alias) {
return $LocalAssetEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.AssetType, int, int> $convertertype =
const i0.EnumIndexConverter<i2.AssetType>(i2.AssetType.values);
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class LocalAssetEntityData extends i0.DataClass
implements i0.Insertable<i1.LocalAssetEntityData> {
final String name;
final i2.AssetType type;
final DateTime createdAt;
final DateTime updatedAt;
final int? durationInSeconds;
final String id;
final String? checksum;
final bool isFavorite;
const LocalAssetEntityData(
{required this.name,
required this.type,
required this.createdAt,
required this.updatedAt,
this.durationInSeconds,
required this.id,
this.checksum,
required this.isFavorite});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['name'] = i0.Variable<String>(name);
{
map['type'] = i0.Variable<int>(
i1.$LocalAssetEntityTable.$convertertype.toSql(type));
}
map['created_at'] = i0.Variable<DateTime>(createdAt);
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
if (!nullToAbsent || durationInSeconds != null) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
}
map['id'] = i0.Variable<String>(id);
if (!nullToAbsent || checksum != null) {
map['checksum'] = i0.Variable<String>(checksum);
}
map['is_favorite'] = i0.Variable<bool>(isFavorite);
return map;
}
factory LocalAssetEntityData.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return LocalAssetEntityData(
name: serializer.fromJson<String>(json['name']),
type: i1.$LocalAssetEntityTable.$convertertype
.fromJson(serializer.fromJson<int>(json['type'])),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
id: serializer.fromJson<String>(json['id']),
checksum: serializer.fromJson<String?>(json['checksum']),
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'name': serializer.toJson<String>(name),
'type': serializer
.toJson<int>(i1.$LocalAssetEntityTable.$convertertype.toJson(type)),
'createdAt': serializer.toJson<DateTime>(createdAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt),
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
'id': serializer.toJson<String>(id),
'checksum': serializer.toJson<String?>(checksum),
'isFavorite': serializer.toJson<bool>(isFavorite),
};
}
i1.LocalAssetEntityData copyWith(
{String? name,
i2.AssetType? type,
DateTime? createdAt,
DateTime? updatedAt,
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
String? id,
i0.Value<String?> checksum = const i0.Value.absent(),
bool? isFavorite}) =>
i1.LocalAssetEntityData(
name: name ?? this.name,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
durationInSeconds: durationInSeconds.present
? durationInSeconds.value
: this.durationInSeconds,
id: id ?? this.id,
checksum: checksum.present ? checksum.value : this.checksum,
isFavorite: isFavorite ?? this.isFavorite,
);
LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) {
return LocalAssetEntityData(
name: data.name.present ? data.name.value : this.name,
type: data.type.present ? data.type.value : this.type,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
durationInSeconds: data.durationInSeconds.present
? data.durationInSeconds.value
: this.durationInSeconds,
id: data.id.present ? data.id.value : this.id,
checksum: data.checksum.present ? data.checksum.value : this.checksum,
isFavorite:
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
);
}
@override
String toString() {
return (StringBuffer('LocalAssetEntityData(')
..write('name: $name, ')
..write('type: $type, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('durationInSeconds: $durationInSeconds, ')
..write('id: $id, ')
..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(name, type, createdAt, updatedAt,
durationInSeconds, id, checksum, isFavorite);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.LocalAssetEntityData &&
other.name == this.name &&
other.type == this.type &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.durationInSeconds == this.durationInSeconds &&
other.id == this.id &&
other.checksum == this.checksum &&
other.isFavorite == this.isFavorite);
}
class LocalAssetEntityCompanion
extends i0.UpdateCompanion<i1.LocalAssetEntityData> {
final i0.Value<String> name;
final i0.Value<i2.AssetType> type;
final i0.Value<DateTime> createdAt;
final i0.Value<DateTime> updatedAt;
final i0.Value<int?> durationInSeconds;
final i0.Value<String> id;
final i0.Value<String?> checksum;
final i0.Value<bool> isFavorite;
const LocalAssetEntityCompanion({
this.name = const i0.Value.absent(),
this.type = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(),
this.id = const i0.Value.absent(),
this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(),
});
LocalAssetEntityCompanion.insert({
required String name,
required i2.AssetType type,
this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(),
required String id,
this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(),
}) : name = i0.Value(name),
type = i0.Value(type),
id = i0.Value(id);
static i0.Insertable<i1.LocalAssetEntityData> custom({
i0.Expression<String>? name,
i0.Expression<int>? type,
i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? durationInSeconds,
i0.Expression<String>? id,
i0.Expression<String>? checksum,
i0.Expression<bool>? isFavorite,
}) {
return i0.RawValuesInsertable({
if (name != null) 'name': name,
if (type != null) 'type': type,
if (createdAt != null) 'created_at': createdAt,
if (updatedAt != null) 'updated_at': updatedAt,
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
if (id != null) 'id': id,
if (checksum != null) 'checksum': checksum,
if (isFavorite != null) 'is_favorite': isFavorite,
});
}
i1.LocalAssetEntityCompanion copyWith(
{i0.Value<String>? name,
i0.Value<i2.AssetType>? type,
i0.Value<DateTime>? createdAt,
i0.Value<DateTime>? updatedAt,
i0.Value<int?>? durationInSeconds,
i0.Value<String>? id,
i0.Value<String?>? checksum,
i0.Value<bool>? isFavorite}) {
return i1.LocalAssetEntityCompanion(
name: name ?? this.name,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
id: id ?? this.id,
checksum: checksum ?? this.checksum,
isFavorite: isFavorite ?? this.isFavorite,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
if (type.present) {
map['type'] = i0.Variable<int>(
i1.$LocalAssetEntityTable.$convertertype.toSql(type.value));
}
if (createdAt.present) {
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
}
if (durationInSeconds.present) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value);
}
if (id.present) {
map['id'] = i0.Variable<String>(id.value);
}
if (checksum.present) {
map['checksum'] = i0.Variable<String>(checksum.value);
}
if (isFavorite.present) {
map['is_favorite'] = i0.Variable<bool>(isFavorite.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('LocalAssetEntityCompanion(')
..write('name: $name, ')
..write('type: $type, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('durationInSeconds: $durationInSeconds, ')
..write('id: $id, ')
..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite')
..write(')'))
.toString();
}
}

View File

@@ -3,6 +3,9 @@ import 'dart:async';
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:immich_mobile/domain/interfaces/db.interface.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';
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
@@ -25,7 +28,16 @@ class IsarDatabaseRepository implements IDatabaseRepository {
Zone.current[_kzoneTxn] == null ? _db.writeTxn(callback) : callback();
}
@DriftDatabase(tables: [UserEntity, UserMetadataEntity, PartnerEntity])
@DriftDatabase(
tables: [
UserEntity,
UserMetadataEntity,
PartnerEntity,
LocalAlbumEntity,
LocalAssetEntity,
LocalAlbumAssetEntity,
],
)
class Drift extends $Drift implements IDatabaseRepository {
Drift([QueryExecutor? executor])
: super(
@@ -42,8 +54,9 @@ class Drift extends $Drift implements IDatabaseRepository {
@override
MigrationStrategy get migration => MigrationStrategy(
beforeOpen: (details) async {
await customStatement('PRAGMA journal_mode = WAL');
await customStatement('PRAGMA foreign_keys = ON');
await customStatement('PRAGMA synchronous = NORMAL');
await customStatement('PRAGMA journal_mode = WAL');
},
);
}

View File

@@ -7,6 +7,12 @@ import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift
as i2;
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
as i3;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
as i4;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
as i5;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
as i6;
abstract class $Drift extends i0.GeneratedDatabase {
$Drift(i0.QueryExecutor e) : super(e);
@@ -16,12 +22,25 @@ abstract class $Drift extends i0.GeneratedDatabase {
i2.$UserMetadataEntityTable(this);
late final i3.$PartnerEntityTable partnerEntity =
i3.$PartnerEntityTable(this);
late final i4.$LocalAlbumEntityTable localAlbumEntity =
i4.$LocalAlbumEntityTable(this);
late final i5.$LocalAssetEntityTable localAssetEntity =
i5.$LocalAssetEntityTable(this);
late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
i6.$LocalAlbumAssetEntityTable(this);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@override
List<i0.DatabaseSchemaEntity> get allSchemaEntities =>
[userEntity, userMetadataEntity, partnerEntity];
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [
userEntity,
userMetadataEntity,
partnerEntity,
localAlbumEntity,
localAssetEntity,
localAlbumAssetEntity,
i5.localAssetChecksum
];
@override
i0.StreamQueryUpdateRules get streamUpdateRules =>
const i0.StreamQueryUpdateRules(
@@ -48,6 +67,22 @@ abstract class $Drift extends i0.GeneratedDatabase {
i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName('local_asset_entity',
limitUpdateKind: i0.UpdateKind.delete),
result: [
i0.TableUpdate('local_album_asset_entity',
kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName('local_album_entity',
limitUpdateKind: i0.UpdateKind.delete),
result: [
i0.TableUpdate('local_album_asset_entity',
kind: i0.UpdateKind.delete),
],
),
],
);
@override
@@ -64,4 +99,10 @@ class $DriftManager {
i2.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
i3.$$PartnerEntityTableTableManager get partnerEntity =>
i3.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
i4.$$LocalAlbumEntityTableTableManager get localAlbumEntity =>
i4.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
i5.$$LocalAssetEntityTableTableManager get localAssetEntity =>
i5.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
}

View File

@@ -0,0 +1,366 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.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.drift.dart';
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:platform/platform.dart';
class DriftLocalAlbumRepository extends DriftDatabaseRepository
implements ILocalAlbumRepository {
final Drift _db;
final Platform _platform;
const DriftLocalAlbumRepository(this._db, {Platform? platform})
: _platform = platform ?? const LocalPlatform(),
super(_db);
@override
Future<List<LocalAlbum>> getAll({SortLocalAlbumsBy? sortBy}) {
final assetCount = _db.localAlbumAssetEntity.assetId.count();
final query = _db.localAlbumEntity.select().join([
leftOuterJoin(
_db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
useColumns: false,
),
]);
query
..addColumns([assetCount])
..groupBy([_db.localAlbumEntity.id]);
if (sortBy == SortLocalAlbumsBy.id) {
query.orderBy([OrderingTerm.asc(_db.localAlbumEntity.id)]);
}
return query
.map(
(row) => row
.readTable(_db.localAlbumEntity)
.toDto(assetCount: row.read(assetCount) ?? 0),
)
.get();
}
@override
Future<void> delete(String albumId) => transaction(() async {
// Remove all assets that are only in this particular album
// We cannot remove all assets in the album because they might be in other albums in iOS
// That is not the case on Android since asset <-> album has one:one mapping
final assetsToDelete = _platform.isIOS
? await _getUniqueAssetsInAlbum(albumId)
: await getAssetIdsForAlbum(albumId);
await _deleteAssets(assetsToDelete);
// All the other assets that are still associated will be unlinked automatically on-cascade
await _db.managers.localAlbumEntity
.filter((a) => a.id.equals(albumId))
.delete();
});
@override
Future<void> syncAlbumDeletes(
String albumId,
Iterable<String> assetIdsToKeep,
) async {
if (assetIdsToKeep.isEmpty) {
return Future.value();
}
final deleteSmt = _db.localAssetEntity.delete();
deleteSmt.where((localAsset) {
final subQuery = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..join([
innerJoin(
_db.localAlbumEntity,
_db.localAlbumAssetEntity.albumId
.equalsExp(_db.localAlbumEntity.id),
),
]);
subQuery.where(
_db.localAlbumEntity.id.equals(albumId) &
_db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep),
);
return localAsset.id.isInQuery(subQuery);
});
await deleteSmt.go();
}
@override
Future<void> upsert(
LocalAlbum localAlbum, {
Iterable<LocalAsset> toUpsert = const [],
Iterable<String> toDelete = const [],
}) {
final companion = LocalAlbumEntityCompanion.insert(
id: localAlbum.id,
name: localAlbum.name,
updatedAt: Value(localAlbum.updatedAt),
backupSelection: localAlbum.backupSelection,
);
return _db.transaction(() async {
await _db.localAlbumEntity
.insertOne(companion, onConflict: DoUpdate((_) => companion));
await _addAssets(localAlbum.id, toUpsert);
await _removeAssets(localAlbum.id, toDelete);
});
}
@override
Future<void> updateAll(Iterable<LocalAlbum> albums) {
return _db.transaction(() async {
await _db.localAlbumEntity
.update()
.write(const LocalAlbumEntityCompanion(marker_: Value(true)));
await _db.batch((batch) {
for (final album in albums) {
final companion = LocalAlbumEntityCompanion.insert(
id: album.id,
name: album.name,
updatedAt: Value(album.updatedAt),
backupSelection: album.backupSelection,
marker_: const Value(null),
);
batch.insert(
_db.localAlbumEntity,
companion,
onConflict: DoUpdate((_) => companion),
);
}
});
if (_platform.isAndroid) {
// On Android, an asset can only be in one album
// So, get the albums that are marked for deletion
// and delete all the assets that are in those albums
final deleteSmt = _db.localAssetEntity.delete();
deleteSmt.where((localAsset) {
final subQuery = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..join([
innerJoin(
_db.localAlbumEntity,
_db.localAlbumAssetEntity.albumId
.equalsExp(_db.localAlbumEntity.id),
),
]);
subQuery.where(_db.localAlbumEntity.marker_.isNotNull());
return localAsset.id.isInQuery(subQuery);
});
await deleteSmt.go();
}
await _db.localAlbumEntity.deleteWhere((f) => f.marker_.isNotNull());
});
}
@override
Future<List<LocalAsset>> getAssetsForAlbum(String albumId) {
final query = _db.localAlbumAssetEntity.select().join(
[
innerJoin(
_db.localAssetEntity,
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
),
],
)
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
return query
.map((row) => row.readTable(_db.localAssetEntity).toDto())
.get();
}
@override
Future<List<String>> getAssetIdsForAlbum(String albumId) {
final query = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId));
return query
.map((row) => row.read(_db.localAlbumAssetEntity.assetId)!)
.get();
}
@override
Future<void> processDelta({
required List<LocalAsset> updates,
required List<String> deletes,
required Map<String, List<String>> assetAlbums,
}) {
return _db.transaction(() async {
await _deleteAssets(deletes);
await _upsertAssets(updates);
// The ugly casting below is required for now because the generated code
// casts the returned values from the platform during decoding them
// and iterating over them causes the type to be List<Object?> instead of
// List<String>
await _db.batch((batch) async {
assetAlbums.cast<String, List<Object?>>().forEach((assetId, albumIds) {
batch.deleteWhere(
_db.localAlbumAssetEntity,
(f) =>
f.albumId.isNotIn(albumIds.cast<String?>().nonNulls) &
f.assetId.equals(assetId),
);
});
});
await _db.batch((batch) async {
assetAlbums.cast<String, List<Object?>>().forEach((assetId, albumIds) {
batch.insertAll(
_db.localAlbumAssetEntity,
albumIds.cast<String?>().nonNulls.map(
(albumId) => LocalAlbumAssetEntityCompanion.insert(
assetId: assetId,
albumId: albumId,
),
),
onConflict: DoNothing(),
);
});
});
});
}
Future<void> _addAssets(String albumId, Iterable<LocalAsset> assets) {
if (assets.isEmpty) {
return Future.value();
}
return transaction(() async {
await _upsertAssets(assets);
await _db.localAlbumAssetEntity.insertAll(
assets.map(
(a) => LocalAlbumAssetEntityCompanion.insert(
assetId: a.id,
albumId: albumId,
),
),
mode: InsertMode.insertOrIgnore,
);
});
}
Future<void> _removeAssets(String albumId, Iterable<String> assetIds) async {
if (assetIds.isEmpty) {
return Future.value();
}
if (_platform.isAndroid) {
return _deleteAssets(assetIds);
}
List<String> assetsToDelete = [];
List<String> assetsToUnLink = [];
final uniqueAssets = await _getUniqueAssetsInAlbum(albumId);
if (uniqueAssets.isEmpty) {
assetsToUnLink = assetIds.toList();
} else {
// Delete unique assets and unlink others
final uniqueSet = uniqueAssets.toSet();
for (final assetId in assetIds) {
if (uniqueSet.contains(assetId)) {
assetsToDelete.add(assetId);
} else {
assetsToUnLink.add(assetId);
}
}
}
return transaction(() async {
if (assetsToUnLink.isNotEmpty) {
await _db.batch(
(batch) => batch.deleteWhere(
_db.localAlbumAssetEntity,
(f) => f.assetId.isIn(assetsToUnLink) & f.albumId.equals(albumId),
),
);
}
await _deleteAssets(assetsToDelete);
});
}
/// Get all asset ids that are only in this album and not in other albums.
/// This is useful in cases where the album is a smart album or a user-created album, especially on iOS
Future<List<String>> _getUniqueAssetsInAlbum(String albumId) {
final assetId = _db.localAlbumAssetEntity.assetId;
final query = _db.localAlbumAssetEntity.selectOnly()
..addColumns([assetId])
..groupBy(
[assetId],
having: _db.localAlbumAssetEntity.albumId.count().equals(1) &
_db.localAlbumAssetEntity.albumId.equals(albumId),
);
return query.map((row) => row.read(assetId)!).get();
}
Future<void> _upsertAssets(Iterable<LocalAsset> localAssets) {
if (localAssets.isEmpty) {
return Future.value();
}
return _db.batch((batch) async {
batch.insertAllOnConflictUpdate(
_db.localAssetEntity,
localAssets.map(
(a) => LocalAssetEntityCompanion.insert(
name: a.name,
type: a.type,
createdAt: Value(a.createdAt),
updatedAt: Value(a.updatedAt),
durationInSeconds: Value.absentIfNull(a.durationInSeconds),
id: a.id,
checksum: Value.absentIfNull(a.checksum),
),
),
);
});
}
Future<void> _deleteAssets(Iterable<String> ids) {
if (ids.isEmpty) {
return Future.value();
}
return _db.batch(
(batch) => batch.deleteWhere(
_db.localAssetEntity,
(f) => f.id.isIn(ids),
),
);
}
}
extension on LocalAlbumEntityData {
LocalAlbum toDto({int assetCount = 0}) {
return LocalAlbum(
id: id,
name: name,
updatedAt: updatedAt,
assetCount: assetCount,
backupSelection: backupSelection,
);
}
}
extension on LocalAssetEntityData {
LocalAsset toDto() {
return LocalAsset(
id: id,
name: name,
checksum: checksum,
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
durationInSeconds: durationInSeconds,
isFavorite: isFavorite,
);
}
}

View File

@@ -0,0 +1,10 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
mixin AssetEntityMixin on Table {
TextColumn get name => text()();
IntColumn get type => intEnum<AssetType>()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get durationInSeconds => integer().nullable()();
}