feat(mobile): do not restore locally deleted assets during trash sync (Android) (#24218)

* feat(trash_sync): do not restore assets deleted locally only
small fixes

* feat(trash_sync): revert tag name

* feat(trash_sync): resolve merge conflicts

* refactor(trash_sync): consolidate local asset deletion logic

* feat(mobile): Add TrashOrigin enum
Replace isRestorable to sourse
change related logic in repo

* feat(mobile): fix format

* fix(mobile): fix restoration scope

* fix(mobile): Add coverage for ActionService deleteLocal paths
Update LocalSyncService tests
Set default value for source column

* fix(mobile): db - require trash origin and update drift schema

---------

Co-authored-by: Peter Ombodi <peter.ombodi@gmail.com>
This commit is contained in:
Peter Ombodi
2026-01-12 18:16:36 +02:00
committed by GitHub
parent 7992fe85d6
commit 8ed81ac3e1
12 changed files with 8704 additions and 35 deletions
@@ -4,6 +4,13 @@ import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
enum TrashOrigin {
// do not change this order!
localSync,
remoteSync,
localUser,
}
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)')
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)')
class TrashedLocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
@@ -19,6 +26,8 @@ class TrashedLocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntity
IntColumn get orientation => integer().withDefault(const Constant(0))();
IntColumn get source => intEnum<TrashOrigin>()();
@override
Set<Column> get primaryKey => {id, albumId};
}
@@ -22,6 +22,7 @@ typedef $$TrashedLocalAssetEntityTableCreateCompanionBuilder =
i0.Value<String?> checksum,
i0.Value<bool> isFavorite,
i0.Value<int> orientation,
required i3.TrashOrigin source,
});
typedef $$TrashedLocalAssetEntityTableUpdateCompanionBuilder =
i1.TrashedLocalAssetEntityCompanion Function({
@@ -37,6 +38,7 @@ typedef $$TrashedLocalAssetEntityTableUpdateCompanionBuilder =
i0.Value<String?> checksum,
i0.Value<bool> isFavorite,
i0.Value<int> orientation,
i0.Value<i3.TrashOrigin> source,
});
class $$TrashedLocalAssetEntityTableFilterComposer
@@ -109,6 +111,12 @@ class $$TrashedLocalAssetEntityTableFilterComposer
column: $table.orientation,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnWithTypeConverterFilters<i3.TrashOrigin, i3.TrashOrigin, int>
get source => $composableBuilder(
column: $table.source,
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
);
}
class $$TrashedLocalAssetEntityTableOrderingComposer
@@ -180,6 +188,11 @@ class $$TrashedLocalAssetEntityTableOrderingComposer
column: $table.orientation,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get source => $composableBuilder(
column: $table.source,
builder: (column) => i0.ColumnOrderings(column),
);
}
class $$TrashedLocalAssetEntityTableAnnotationComposer
@@ -233,6 +246,9 @@ class $$TrashedLocalAssetEntityTableAnnotationComposer
column: $table.orientation,
builder: (column) => column,
);
i0.GeneratedColumnWithTypeConverter<i3.TrashOrigin, int> get source =>
$composableBuilder(column: $table.source, builder: (column) => column);
}
class $$TrashedLocalAssetEntityTableTableManager
@@ -293,6 +309,7 @@ class $$TrashedLocalAssetEntityTableTableManager
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(),
i0.Value<int> orientation = const i0.Value.absent(),
i0.Value<i3.TrashOrigin> source = const i0.Value.absent(),
}) => i1.TrashedLocalAssetEntityCompanion(
name: name,
type: type,
@@ -306,6 +323,7 @@ class $$TrashedLocalAssetEntityTableTableManager
checksum: checksum,
isFavorite: isFavorite,
orientation: orientation,
source: source,
),
createCompanionCallback:
({
@@ -321,6 +339,7 @@ class $$TrashedLocalAssetEntityTableTableManager
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(),
i0.Value<int> orientation = const i0.Value.absent(),
required i3.TrashOrigin source,
}) => i1.TrashedLocalAssetEntityCompanion.insert(
name: name,
type: type,
@@ -334,6 +353,7 @@ class $$TrashedLocalAssetEntityTableTableManager
checksum: checksum,
isFavorite: isFavorite,
orientation: orientation,
source: source,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
@@ -519,6 +539,17 @@ class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity
defaultValue: const i4.Constant(0),
);
@override
late final i0.GeneratedColumnWithTypeConverter<i3.TrashOrigin, int> source =
i0.GeneratedColumn<int>(
'source',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
).withConverter<i3.TrashOrigin>(
i1.$TrashedLocalAssetEntityTable.$convertersource,
);
@override
List<i0.GeneratedColumn> get $columns => [
name,
type,
@@ -532,6 +563,7 @@ class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity
checksum,
isFavorite,
orientation,
source,
];
@override
String get aliasedName => _alias ?? actualTableName;
@@ -682,6 +714,12 @@ class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity
i0.DriftSqlType.int,
data['${effectivePrefix}orientation'],
)!,
source: i1.$TrashedLocalAssetEntityTable.$convertersource.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}source'],
)!,
),
);
}
@@ -692,6 +730,8 @@ class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity
static i0.JsonTypeConverter2<i2.AssetType, int, int> $convertertype =
const i0.EnumIndexConverter<i2.AssetType>(i2.AssetType.values);
static i0.JsonTypeConverter2<i3.TrashOrigin, int, int> $convertersource =
const i0.EnumIndexConverter<i3.TrashOrigin>(i3.TrashOrigin.values);
@override
bool get withoutRowId => true;
@override
@@ -712,6 +752,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
final String? checksum;
final bool isFavorite;
final int orientation;
final i3.TrashOrigin source;
const TrashedLocalAssetEntityData({
required this.name,
required this.type,
@@ -725,6 +766,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
this.checksum,
required this.isFavorite,
required this.orientation,
required this.source,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
@@ -753,6 +795,11 @@ class TrashedLocalAssetEntityData extends i0.DataClass
}
map['is_favorite'] = i0.Variable<bool>(isFavorite);
map['orientation'] = i0.Variable<int>(orientation);
{
map['source'] = i0.Variable<int>(
i1.$TrashedLocalAssetEntityTable.$convertersource.toSql(source),
);
}
return map;
}
@@ -776,6 +823,9 @@ class TrashedLocalAssetEntityData extends i0.DataClass
checksum: serializer.fromJson<String?>(json['checksum']),
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
orientation: serializer.fromJson<int>(json['orientation']),
source: i1.$TrashedLocalAssetEntityTable.$convertersource.fromJson(
serializer.fromJson<int>(json['source']),
),
);
}
@override
@@ -796,6 +846,9 @@ class TrashedLocalAssetEntityData extends i0.DataClass
'checksum': serializer.toJson<String?>(checksum),
'isFavorite': serializer.toJson<bool>(isFavorite),
'orientation': serializer.toJson<int>(orientation),
'source': serializer.toJson<int>(
i1.$TrashedLocalAssetEntityTable.$convertersource.toJson(source),
),
};
}
@@ -812,6 +865,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
i0.Value<String?> checksum = const i0.Value.absent(),
bool? isFavorite,
int? orientation,
i3.TrashOrigin? source,
}) => i1.TrashedLocalAssetEntityData(
name: name ?? this.name,
type: type ?? this.type,
@@ -827,6 +881,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
checksum: checksum.present ? checksum.value : this.checksum,
isFavorite: isFavorite ?? this.isFavorite,
orientation: orientation ?? this.orientation,
source: source ?? this.source,
);
TrashedLocalAssetEntityData copyWithCompanion(
i1.TrashedLocalAssetEntityCompanion data,
@@ -850,6 +905,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
orientation: data.orientation.present
? data.orientation.value
: this.orientation,
source: data.source.present ? data.source.value : this.source,
);
}
@@ -867,7 +923,8 @@ class TrashedLocalAssetEntityData extends i0.DataClass
..write('albumId: $albumId, ')
..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite, ')
..write('orientation: $orientation')
..write('orientation: $orientation, ')
..write('source: $source')
..write(')'))
.toString();
}
@@ -886,6 +943,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
checksum,
isFavorite,
orientation,
source,
);
@override
bool operator ==(Object other) =>
@@ -902,7 +960,8 @@ class TrashedLocalAssetEntityData extends i0.DataClass
other.albumId == this.albumId &&
other.checksum == this.checksum &&
other.isFavorite == this.isFavorite &&
other.orientation == this.orientation);
other.orientation == this.orientation &&
other.source == this.source);
}
class TrashedLocalAssetEntityCompanion
@@ -919,6 +978,7 @@ class TrashedLocalAssetEntityCompanion
final i0.Value<String?> checksum;
final i0.Value<bool> isFavorite;
final i0.Value<int> orientation;
final i0.Value<i3.TrashOrigin> source;
const TrashedLocalAssetEntityCompanion({
this.name = const i0.Value.absent(),
this.type = const i0.Value.absent(),
@@ -932,6 +992,7 @@ class TrashedLocalAssetEntityCompanion
this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(),
this.orientation = const i0.Value.absent(),
this.source = const i0.Value.absent(),
});
TrashedLocalAssetEntityCompanion.insert({
required String name,
@@ -946,10 +1007,12 @@ class TrashedLocalAssetEntityCompanion
this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(),
this.orientation = const i0.Value.absent(),
required i3.TrashOrigin source,
}) : name = i0.Value(name),
type = i0.Value(type),
id = i0.Value(id),
albumId = i0.Value(albumId);
albumId = i0.Value(albumId),
source = i0.Value(source);
static i0.Insertable<i1.TrashedLocalAssetEntityData> custom({
i0.Expression<String>? name,
i0.Expression<int>? type,
@@ -963,6 +1026,7 @@ class TrashedLocalAssetEntityCompanion
i0.Expression<String>? checksum,
i0.Expression<bool>? isFavorite,
i0.Expression<int>? orientation,
i0.Expression<int>? source,
}) {
return i0.RawValuesInsertable({
if (name != null) 'name': name,
@@ -977,6 +1041,7 @@ class TrashedLocalAssetEntityCompanion
if (checksum != null) 'checksum': checksum,
if (isFavorite != null) 'is_favorite': isFavorite,
if (orientation != null) 'orientation': orientation,
if (source != null) 'source': source,
});
}
@@ -993,6 +1058,7 @@ class TrashedLocalAssetEntityCompanion
i0.Value<String?>? checksum,
i0.Value<bool>? isFavorite,
i0.Value<int>? orientation,
i0.Value<i3.TrashOrigin>? source,
}) {
return i1.TrashedLocalAssetEntityCompanion(
name: name ?? this.name,
@@ -1007,6 +1073,7 @@ class TrashedLocalAssetEntityCompanion
checksum: checksum ?? this.checksum,
isFavorite: isFavorite ?? this.isFavorite,
orientation: orientation ?? this.orientation,
source: source ?? this.source,
);
}
@@ -1051,6 +1118,11 @@ class TrashedLocalAssetEntityCompanion
if (orientation.present) {
map['orientation'] = i0.Variable<int>(orientation.value);
}
if (source.present) {
map['source'] = i0.Variable<int>(
i1.$TrashedLocalAssetEntityTable.$convertersource.toSql(source.value),
);
}
return map;
}
@@ -1068,7 +1140,8 @@ class TrashedLocalAssetEntityCompanion
..write('albumId: $albumId, ')
..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite, ')
..write('orientation: $orientation')
..write('orientation: $orientation, ')
..write('source: $source')
..write(')'))
.toString();
}