mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
Merge branch 'main' into upload-to-album
This commit is contained in:
@@ -5,6 +5,11 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_asset_face_person_id ON asset_face_entity (person_id)')
|
||||
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_asset_face_asset_id ON asset_face_entity (asset_id)')
|
||||
@TableIndex.sql('''
|
||||
CREATE INDEX IF NOT EXISTS idx_asset_face_visible_person
|
||||
ON asset_face_entity (person_id, asset_id)
|
||||
WHERE is_visible = 1 AND deleted_at IS NULL
|
||||
''')
|
||||
class AssetFaceEntity extends Table with DriftDefaultsMixin {
|
||||
const AssetFaceEntity();
|
||||
|
||||
|
||||
@@ -1350,3 +1350,7 @@ i0.Index get idxAssetFaceAssetId => i0.Index(
|
||||
'idx_asset_face_asset_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_asset_face_asset_id ON asset_face_entity (asset_id)',
|
||||
);
|
||||
i0.Index get idxAssetFaceVisiblePerson => i0.Index(
|
||||
'idx_asset_face_visible_person',
|
||||
'CREATE INDEX IF NOT EXISTS idx_asset_face_visible_person ON asset_face_entity (person_id, asset_id) WHERE is_visible = 1 AND deleted_at IS NULL',
|
||||
);
|
||||
|
||||
@@ -6,6 +6,10 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
|
||||
|
||||
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)')
|
||||
@TableIndex.sql('''
|
||||
CREATE INDEX IF NOT EXISTS idx_remote_exif_city
|
||||
ON remote_exif_entity (city) WHERE city IS NOT NULL
|
||||
''')
|
||||
class RemoteExifEntity extends Table with DriftDefaultsMixin {
|
||||
const RemoteExifEntity();
|
||||
|
||||
|
||||
@@ -1883,3 +1883,8 @@ class RemoteExifEntityCompanion
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
i0.Index get idxRemoteExifCity => i0.Index(
|
||||
'idx_remote_exif_city',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_exif_city ON remote_exif_entity (city) WHERE city IS NOT NULL',
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'local_asset.entity.dart';
|
||||
import 'local_album.entity.dart';
|
||||
import 'local_album_asset.entity.dart';
|
||||
|
||||
mergedAsset:
|
||||
mergedAsset:
|
||||
SELECT
|
||||
rae.id as remote_id,
|
||||
(SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id,
|
||||
@@ -27,7 +27,8 @@ SELECT
|
||||
NULL as longitude,
|
||||
NULL as adjustmentTime,
|
||||
rae.is_edited,
|
||||
0 as playback_style
|
||||
0 as playback_style,
|
||||
rae.uploaded_at
|
||||
FROM
|
||||
remote_asset_entity rae
|
||||
LEFT JOIN
|
||||
@@ -65,7 +66,8 @@ SELECT
|
||||
lae.longitude,
|
||||
lae.adjustment_time,
|
||||
0 as is_edited,
|
||||
lae.playback_style
|
||||
lae.playback_style,
|
||||
NULL as uploaded_at
|
||||
FROM
|
||||
local_asset_entity lae
|
||||
WHERE NOT EXISTS (
|
||||
|
||||
+4
-1
@@ -29,7 +29,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
|
||||
);
|
||||
$arrayStartIndex += generatedlimit.amountOfVariables;
|
||||
return customSelect(
|
||||
'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_ms, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id, NULL AS latitude, NULL AS longitude, NULL AS adjustmentTime, rae.is_edited, 0 AS playback_style FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_ms, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id, lae.latitude, lae.longitude, lae.adjustment_time, 0 AS is_edited, lae.playback_style FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}',
|
||||
'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_ms, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id, NULL AS latitude, NULL AS longitude, NULL AS adjustmentTime, rae.is_edited, 0 AS playback_style, rae.uploaded_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_ms, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id, lae.latitude, lae.longitude, lae.adjustment_time, 0 AS is_edited, lae.playback_style, NULL AS uploaded_at FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}',
|
||||
variables: [
|
||||
for (var $ in userIds) i0.Variable<String>($),
|
||||
...generatedlimit.introducedVariables,
|
||||
@@ -68,6 +68,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
|
||||
adjustmentTime: row.readNullable<DateTime>('adjustmentTime'),
|
||||
isEdited: row.read<bool>('is_edited'),
|
||||
playbackStyle: row.read<int>('playback_style'),
|
||||
uploadedAt: row.readNullable<DateTime>('uploaded_at'),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -141,6 +142,7 @@ class MergedAssetResult {
|
||||
final DateTime? adjustmentTime;
|
||||
final bool isEdited;
|
||||
final int playbackStyle;
|
||||
final DateTime? uploadedAt;
|
||||
MergedAssetResult({
|
||||
this.remoteId,
|
||||
this.localId,
|
||||
@@ -164,6 +166,7 @@ class MergedAssetResult {
|
||||
this.adjustmentTime,
|
||||
required this.isEdited,
|
||||
required this.playbackStyle,
|
||||
this.uploadedAt,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
class MetadataEntity extends Table with DriftDefaultsMixin {
|
||||
const MetadataEntity();
|
||||
|
||||
TextColumn get key => text()();
|
||||
|
||||
TextColumn get value => text()();
|
||||
|
||||
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {key};
|
||||
|
||||
@override
|
||||
String get tableName => "metadata";
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart'
|
||||
as i1;
|
||||
import 'package:immich_mobile/infrastructure/entities/metadata.entity.dart'
|
||||
as i2;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
|
||||
|
||||
typedef $$MetadataEntityTableCreateCompanionBuilder =
|
||||
i1.MetadataEntityCompanion Function({
|
||||
required String key,
|
||||
required String value,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
});
|
||||
typedef $$MetadataEntityTableUpdateCompanionBuilder =
|
||||
i1.MetadataEntityCompanion Function({
|
||||
i0.Value<String> key,
|
||||
i0.Value<String> value,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
});
|
||||
|
||||
class $$MetadataEntityTableFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$MetadataEntityTable> {
|
||||
$$MetadataEntityTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnFilters<String> get key => $composableBuilder(
|
||||
column: $table.key,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<String> get value => $composableBuilder(
|
||||
column: $table.value,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
|
||||
column: $table.updatedAt,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$MetadataEntityTableOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$MetadataEntityTable> {
|
||||
$$MetadataEntityTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<String> get key => $composableBuilder(
|
||||
column: $table.key,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<String> get value => $composableBuilder(
|
||||
column: $table.value,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
|
||||
column: $table.updatedAt,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$MetadataEntityTableAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$MetadataEntityTable> {
|
||||
$$MetadataEntityTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumn<String> get key =>
|
||||
$composableBuilder(column: $table.key, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get value =>
|
||||
$composableBuilder(column: $table.value, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get updatedAt =>
|
||||
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $$MetadataEntityTableTableManager
|
||||
extends
|
||||
i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$MetadataEntityTable,
|
||||
i1.MetadataEntityData,
|
||||
i1.$$MetadataEntityTableFilterComposer,
|
||||
i1.$$MetadataEntityTableOrderingComposer,
|
||||
i1.$$MetadataEntityTableAnnotationComposer,
|
||||
$$MetadataEntityTableCreateCompanionBuilder,
|
||||
$$MetadataEntityTableUpdateCompanionBuilder,
|
||||
(
|
||||
i1.MetadataEntityData,
|
||||
i0.BaseReferences<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$MetadataEntityTable,
|
||||
i1.MetadataEntityData
|
||||
>,
|
||||
),
|
||||
i1.MetadataEntityData,
|
||||
i0.PrefetchHooks Function()
|
||||
> {
|
||||
$$MetadataEntityTableTableManager(
|
||||
i0.GeneratedDatabase db,
|
||||
i1.$MetadataEntityTable table,
|
||||
) : super(
|
||||
i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$$MetadataEntityTableFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
i1.$$MetadataEntityTableOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () => i1
|
||||
.$$MetadataEntityTableAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback:
|
||||
({
|
||||
i0.Value<String> key = const i0.Value.absent(),
|
||||
i0.Value<String> value = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
}) => i1.MetadataEntityCompanion(
|
||||
key: key,
|
||||
value: value,
|
||||
updatedAt: updatedAt,
|
||||
),
|
||||
createCompanionCallback:
|
||||
({
|
||||
required String key,
|
||||
required String value,
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
}) => i1.MetadataEntityCompanion.insert(
|
||||
key: key,
|
||||
value: value,
|
||||
updatedAt: updatedAt,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
typedef $$MetadataEntityTableProcessedTableManager =
|
||||
i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$MetadataEntityTable,
|
||||
i1.MetadataEntityData,
|
||||
i1.$$MetadataEntityTableFilterComposer,
|
||||
i1.$$MetadataEntityTableOrderingComposer,
|
||||
i1.$$MetadataEntityTableAnnotationComposer,
|
||||
$$MetadataEntityTableCreateCompanionBuilder,
|
||||
$$MetadataEntityTableUpdateCompanionBuilder,
|
||||
(
|
||||
i1.MetadataEntityData,
|
||||
i0.BaseReferences<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$MetadataEntityTable,
|
||||
i1.MetadataEntityData
|
||||
>,
|
||||
),
|
||||
i1.MetadataEntityData,
|
||||
i0.PrefetchHooks Function()
|
||||
>;
|
||||
|
||||
class $MetadataEntityTable extends i2.MetadataEntity
|
||||
with i0.TableInfo<$MetadataEntityTable, i1.MetadataEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$MetadataEntityTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _keyMeta = const i0.VerificationMeta('key');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> key = i0.GeneratedColumn<String>(
|
||||
'key',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const i0.VerificationMeta _valueMeta = const i0.VerificationMeta(
|
||||
'value',
|
||||
);
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> value = i0.GeneratedColumn<String>(
|
||||
'value',
|
||||
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: i3.currentDateAndTime,
|
||||
);
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [key, value, updatedAt];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'metadata';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(
|
||||
i0.Insertable<i1.MetadataEntityData> instance, {
|
||||
bool isInserting = false,
|
||||
}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('key')) {
|
||||
context.handle(
|
||||
_keyMeta,
|
||||
key.isAcceptableOrUnknown(data['key']!, _keyMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_keyMeta);
|
||||
}
|
||||
if (data.containsKey('value')) {
|
||||
context.handle(
|
||||
_valueMeta,
|
||||
value.isAcceptableOrUnknown(data['value']!, _valueMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_valueMeta);
|
||||
}
|
||||
if (data.containsKey('updated_at')) {
|
||||
context.handle(
|
||||
_updatedAtMeta,
|
||||
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta),
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {key};
|
||||
@override
|
||||
i1.MetadataEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.MetadataEntityData(
|
||||
key: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}key'],
|
||||
)!,
|
||||
value: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}value'],
|
||||
)!,
|
||||
updatedAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}updated_at'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$MetadataEntityTable createAlias(String alias) {
|
||||
return $MetadataEntityTable(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get withoutRowId => true;
|
||||
@override
|
||||
bool get isStrict => true;
|
||||
}
|
||||
|
||||
class MetadataEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.MetadataEntityData> {
|
||||
final String key;
|
||||
final String value;
|
||||
final DateTime updatedAt;
|
||||
const MetadataEntityData({
|
||||
required this.key,
|
||||
required this.value,
|
||||
required this.updatedAt,
|
||||
});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['key'] = i0.Variable<String>(key);
|
||||
map['value'] = i0.Variable<String>(value);
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
|
||||
return map;
|
||||
}
|
||||
|
||||
factory MetadataEntityData.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
i0.ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return MetadataEntityData(
|
||||
key: serializer.fromJson<String>(json['key']),
|
||||
value: serializer.fromJson<String>(json['value']),
|
||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'key': serializer.toJson<String>(key),
|
||||
'value': serializer.toJson<String>(value),
|
||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
||||
};
|
||||
}
|
||||
|
||||
i1.MetadataEntityData copyWith({
|
||||
String? key,
|
||||
String? value,
|
||||
DateTime? updatedAt,
|
||||
}) => i1.MetadataEntityData(
|
||||
key: key ?? this.key,
|
||||
value: value ?? this.value,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
MetadataEntityData copyWithCompanion(i1.MetadataEntityCompanion data) {
|
||||
return MetadataEntityData(
|
||||
key: data.key.present ? data.key.value : this.key,
|
||||
value: data.value.present ? data.value.value : this.value,
|
||||
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('MetadataEntityData(')
|
||||
..write('key: $key, ')
|
||||
..write('value: $value, ')
|
||||
..write('updatedAt: $updatedAt')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(key, value, updatedAt);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.MetadataEntityData &&
|
||||
other.key == this.key &&
|
||||
other.value == this.value &&
|
||||
other.updatedAt == this.updatedAt);
|
||||
}
|
||||
|
||||
class MetadataEntityCompanion
|
||||
extends i0.UpdateCompanion<i1.MetadataEntityData> {
|
||||
final i0.Value<String> key;
|
||||
final i0.Value<String> value;
|
||||
final i0.Value<DateTime> updatedAt;
|
||||
const MetadataEntityCompanion({
|
||||
this.key = const i0.Value.absent(),
|
||||
this.value = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
});
|
||||
MetadataEntityCompanion.insert({
|
||||
required String key,
|
||||
required String value,
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
}) : key = i0.Value(key),
|
||||
value = i0.Value(value);
|
||||
static i0.Insertable<i1.MetadataEntityData> custom({
|
||||
i0.Expression<String>? key,
|
||||
i0.Expression<String>? value,
|
||||
i0.Expression<DateTime>? updatedAt,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (key != null) 'key': key,
|
||||
if (value != null) 'value': value,
|
||||
if (updatedAt != null) 'updated_at': updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
i1.MetadataEntityCompanion copyWith({
|
||||
i0.Value<String>? key,
|
||||
i0.Value<String>? value,
|
||||
i0.Value<DateTime>? updatedAt,
|
||||
}) {
|
||||
return i1.MetadataEntityCompanion(
|
||||
key: key ?? this.key,
|
||||
value: value ?? this.value,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (key.present) {
|
||||
map['key'] = i0.Variable<String>(key.value);
|
||||
}
|
||||
if (value.present) {
|
||||
map['value'] = i0.Variable<String>(value.value);
|
||||
}
|
||||
if (updatedAt.present) {
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('MetadataEntityCompanion(')
|
||||
..write('key: $key, ')
|
||||
..write('value: $value, ')
|
||||
..write('updatedAt: $updatedAt')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,6 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
@TableIndex.sql(
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)',
|
||||
)
|
||||
@TableIndex.sql('''
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum
|
||||
ON remote_asset_entity (owner_id, checksum)
|
||||
@@ -20,12 +17,10 @@ WHERE (library_id IS NOT NULL);
|
||||
''')
|
||||
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)')
|
||||
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_asset_stack_id ON remote_asset_entity (stack_id)')
|
||||
@TableIndex.sql(
|
||||
"CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_day ON remote_asset_entity (STRFTIME('%Y-%m-%d', local_date_time))",
|
||||
)
|
||||
@TableIndex.sql(
|
||||
"CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_month ON remote_asset_entity (STRFTIME('%Y-%m', local_date_time))",
|
||||
)
|
||||
@TableIndex.sql('''
|
||||
CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_visibility_deleted_created
|
||||
ON remote_asset_entity (owner_id, visibility, deleted_at, created_at DESC)
|
||||
''')
|
||||
class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
||||
const RemoteAssetEntity();
|
||||
|
||||
@@ -43,6 +38,8 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin
|
||||
|
||||
DateTimeColumn get deletedAt => dateTime().nullable()();
|
||||
|
||||
DateTimeColumn get uploadedAt => dateTime().nullable()();
|
||||
|
||||
TextColumn get livePhotoVideoId => text().nullable()();
|
||||
|
||||
IntColumn get visibility => intEnum<AssetVisibility>()();
|
||||
@@ -66,6 +63,7 @@ extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData {
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
uploadedAt: uploadedAt,
|
||||
durationMs: durationMs,
|
||||
isFavorite: isFavorite,
|
||||
height: height,
|
||||
|
||||
+76
-14
@@ -27,6 +27,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder =
|
||||
i0.Value<DateTime?> localDateTime,
|
||||
i0.Value<String?> thumbHash,
|
||||
i0.Value<DateTime?> deletedAt,
|
||||
i0.Value<DateTime?> uploadedAt,
|
||||
i0.Value<String?> livePhotoVideoId,
|
||||
required i2.AssetVisibility visibility,
|
||||
i0.Value<String?> stackId,
|
||||
@@ -49,6 +50,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder =
|
||||
i0.Value<DateTime?> localDateTime,
|
||||
i0.Value<String?> thumbHash,
|
||||
i0.Value<DateTime?> deletedAt,
|
||||
i0.Value<DateTime?> uploadedAt,
|
||||
i0.Value<String?> livePhotoVideoId,
|
||||
i0.Value<i2.AssetVisibility> visibility,
|
||||
i0.Value<String?> stackId,
|
||||
@@ -177,6 +179,11 @@ class $$RemoteAssetEntityTableFilterComposer
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<DateTime> get uploadedAt => $composableBuilder(
|
||||
column: $table.uploadedAt,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<String> get livePhotoVideoId => $composableBuilder(
|
||||
column: $table.livePhotoVideoId,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
@@ -305,6 +312,11 @@ class $$RemoteAssetEntityTableOrderingComposer
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<DateTime> get uploadedAt => $composableBuilder(
|
||||
column: $table.uploadedAt,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<String> get livePhotoVideoId => $composableBuilder(
|
||||
column: $table.livePhotoVideoId,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
@@ -412,6 +424,11 @@ class $$RemoteAssetEntityTableAnnotationComposer
|
||||
i0.GeneratedColumn<DateTime> get deletedAt =>
|
||||
$composableBuilder(column: $table.deletedAt, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get uploadedAt => $composableBuilder(
|
||||
column: $table.uploadedAt,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
i0.GeneratedColumn<String> get livePhotoVideoId => $composableBuilder(
|
||||
column: $table.livePhotoVideoId,
|
||||
builder: (column) => column,
|
||||
@@ -507,6 +524,7 @@ class $$RemoteAssetEntityTableTableManager
|
||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
||||
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime?> uploadedAt = const i0.Value.absent(),
|
||||
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
|
||||
i0.Value<i2.AssetVisibility> visibility =
|
||||
const i0.Value.absent(),
|
||||
@@ -528,6 +546,7 @@ class $$RemoteAssetEntityTableTableManager
|
||||
localDateTime: localDateTime,
|
||||
thumbHash: thumbHash,
|
||||
deletedAt: deletedAt,
|
||||
uploadedAt: uploadedAt,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
visibility: visibility,
|
||||
stackId: stackId,
|
||||
@@ -550,6 +569,7 @@ class $$RemoteAssetEntityTableTableManager
|
||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
||||
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime?> uploadedAt = const i0.Value.absent(),
|
||||
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
|
||||
required i2.AssetVisibility visibility,
|
||||
i0.Value<String?> stackId = const i0.Value.absent(),
|
||||
@@ -570,6 +590,7 @@ class $$RemoteAssetEntityTableTableManager
|
||||
localDateTime: localDateTime,
|
||||
thumbHash: thumbHash,
|
||||
deletedAt: deletedAt,
|
||||
uploadedAt: uploadedAt,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
visibility: visibility,
|
||||
stackId: stackId,
|
||||
@@ -645,9 +666,9 @@ typedef $$RemoteAssetEntityTableProcessedTableManager =
|
||||
i1.RemoteAssetEntityData,
|
||||
i0.PrefetchHooks Function({bool ownerId})
|
||||
>;
|
||||
i0.Index get idxRemoteAssetOwnerChecksum => i0.Index(
|
||||
'idx_remote_asset_owner_checksum',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)',
|
||||
i0.Index get uQRemoteAssetsOwnerChecksum => i0.Index(
|
||||
'UQ_remote_assets_owner_checksum',
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)',
|
||||
);
|
||||
|
||||
class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
@@ -818,6 +839,18 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const i0.VerificationMeta _uploadedAtMeta = const i0.VerificationMeta(
|
||||
'uploadedAt',
|
||||
);
|
||||
@override
|
||||
late final i0.GeneratedColumn<DateTime> uploadedAt =
|
||||
i0.GeneratedColumn<DateTime>(
|
||||
'uploaded_at',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const i0.VerificationMeta _livePhotoVideoIdMeta =
|
||||
const i0.VerificationMeta('livePhotoVideoId');
|
||||
@override
|
||||
@@ -894,6 +927,7 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
localDateTime,
|
||||
thumbHash,
|
||||
deletedAt,
|
||||
uploadedAt,
|
||||
livePhotoVideoId,
|
||||
visibility,
|
||||
stackId,
|
||||
@@ -998,6 +1032,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('uploaded_at')) {
|
||||
context.handle(
|
||||
_uploadedAtMeta,
|
||||
uploadedAt.isAcceptableOrUnknown(data['uploaded_at']!, _uploadedAtMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('live_photo_video_id')) {
|
||||
context.handle(
|
||||
_livePhotoVideoIdMeta,
|
||||
@@ -1095,6 +1135,10 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
i0.DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}deleted_at'],
|
||||
),
|
||||
uploadedAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}uploaded_at'],
|
||||
),
|
||||
livePhotoVideoId: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}live_photo_video_id'],
|
||||
@@ -1153,6 +1197,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
final DateTime? localDateTime;
|
||||
final String? thumbHash;
|
||||
final DateTime? deletedAt;
|
||||
final DateTime? uploadedAt;
|
||||
final String? livePhotoVideoId;
|
||||
final i2.AssetVisibility visibility;
|
||||
final String? stackId;
|
||||
@@ -1173,6 +1218,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
this.localDateTime,
|
||||
this.thumbHash,
|
||||
this.deletedAt,
|
||||
this.uploadedAt,
|
||||
this.livePhotoVideoId,
|
||||
required this.visibility,
|
||||
this.stackId,
|
||||
@@ -1212,6 +1258,9 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
if (!nullToAbsent || deletedAt != null) {
|
||||
map['deleted_at'] = i0.Variable<DateTime>(deletedAt);
|
||||
}
|
||||
if (!nullToAbsent || uploadedAt != null) {
|
||||
map['uploaded_at'] = i0.Variable<DateTime>(uploadedAt);
|
||||
}
|
||||
if (!nullToAbsent || livePhotoVideoId != null) {
|
||||
map['live_photo_video_id'] = i0.Variable<String>(livePhotoVideoId);
|
||||
}
|
||||
@@ -1252,6 +1301,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
localDateTime: serializer.fromJson<DateTime?>(json['localDateTime']),
|
||||
thumbHash: serializer.fromJson<String?>(json['thumbHash']),
|
||||
deletedAt: serializer.fromJson<DateTime?>(json['deletedAt']),
|
||||
uploadedAt: serializer.fromJson<DateTime?>(json['uploadedAt']),
|
||||
livePhotoVideoId: serializer.fromJson<String?>(json['livePhotoVideoId']),
|
||||
visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromJson(
|
||||
serializer.fromJson<int>(json['visibility']),
|
||||
@@ -1281,6 +1331,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
'localDateTime': serializer.toJson<DateTime?>(localDateTime),
|
||||
'thumbHash': serializer.toJson<String?>(thumbHash),
|
||||
'deletedAt': serializer.toJson<DateTime?>(deletedAt),
|
||||
'uploadedAt': serializer.toJson<DateTime?>(uploadedAt),
|
||||
'livePhotoVideoId': serializer.toJson<String?>(livePhotoVideoId),
|
||||
'visibility': serializer.toJson<int>(
|
||||
i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility),
|
||||
@@ -1306,6 +1357,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
||||
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime?> uploadedAt = const i0.Value.absent(),
|
||||
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
|
||||
i2.AssetVisibility? visibility,
|
||||
i0.Value<String?> stackId = const i0.Value.absent(),
|
||||
@@ -1328,6 +1380,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
: this.localDateTime,
|
||||
thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash,
|
||||
deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt,
|
||||
uploadedAt: uploadedAt.present ? uploadedAt.value : this.uploadedAt,
|
||||
livePhotoVideoId: livePhotoVideoId.present
|
||||
? livePhotoVideoId.value
|
||||
: this.livePhotoVideoId,
|
||||
@@ -1358,6 +1411,9 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
: this.localDateTime,
|
||||
thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash,
|
||||
deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt,
|
||||
uploadedAt: data.uploadedAt.present
|
||||
? data.uploadedAt.value
|
||||
: this.uploadedAt,
|
||||
livePhotoVideoId: data.livePhotoVideoId.present
|
||||
? data.livePhotoVideoId.value
|
||||
: this.livePhotoVideoId,
|
||||
@@ -1387,6 +1443,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
..write('localDateTime: $localDateTime, ')
|
||||
..write('thumbHash: $thumbHash, ')
|
||||
..write('deletedAt: $deletedAt, ')
|
||||
..write('uploadedAt: $uploadedAt, ')
|
||||
..write('livePhotoVideoId: $livePhotoVideoId, ')
|
||||
..write('visibility: $visibility, ')
|
||||
..write('stackId: $stackId, ')
|
||||
@@ -1412,6 +1469,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
localDateTime,
|
||||
thumbHash,
|
||||
deletedAt,
|
||||
uploadedAt,
|
||||
livePhotoVideoId,
|
||||
visibility,
|
||||
stackId,
|
||||
@@ -1436,6 +1494,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
other.localDateTime == this.localDateTime &&
|
||||
other.thumbHash == this.thumbHash &&
|
||||
other.deletedAt == this.deletedAt &&
|
||||
other.uploadedAt == this.uploadedAt &&
|
||||
other.livePhotoVideoId == this.livePhotoVideoId &&
|
||||
other.visibility == this.visibility &&
|
||||
other.stackId == this.stackId &&
|
||||
@@ -1459,6 +1518,7 @@ class RemoteAssetEntityCompanion
|
||||
final i0.Value<DateTime?> localDateTime;
|
||||
final i0.Value<String?> thumbHash;
|
||||
final i0.Value<DateTime?> deletedAt;
|
||||
final i0.Value<DateTime?> uploadedAt;
|
||||
final i0.Value<String?> livePhotoVideoId;
|
||||
final i0.Value<i2.AssetVisibility> visibility;
|
||||
final i0.Value<String?> stackId;
|
||||
@@ -1479,6 +1539,7 @@ class RemoteAssetEntityCompanion
|
||||
this.localDateTime = const i0.Value.absent(),
|
||||
this.thumbHash = const i0.Value.absent(),
|
||||
this.deletedAt = const i0.Value.absent(),
|
||||
this.uploadedAt = const i0.Value.absent(),
|
||||
this.livePhotoVideoId = const i0.Value.absent(),
|
||||
this.visibility = const i0.Value.absent(),
|
||||
this.stackId = const i0.Value.absent(),
|
||||
@@ -1500,6 +1561,7 @@ class RemoteAssetEntityCompanion
|
||||
this.localDateTime = const i0.Value.absent(),
|
||||
this.thumbHash = const i0.Value.absent(),
|
||||
this.deletedAt = const i0.Value.absent(),
|
||||
this.uploadedAt = const i0.Value.absent(),
|
||||
this.livePhotoVideoId = const i0.Value.absent(),
|
||||
required i2.AssetVisibility visibility,
|
||||
this.stackId = const i0.Value.absent(),
|
||||
@@ -1526,6 +1588,7 @@ class RemoteAssetEntityCompanion
|
||||
i0.Expression<DateTime>? localDateTime,
|
||||
i0.Expression<String>? thumbHash,
|
||||
i0.Expression<DateTime>? deletedAt,
|
||||
i0.Expression<DateTime>? uploadedAt,
|
||||
i0.Expression<String>? livePhotoVideoId,
|
||||
i0.Expression<int>? visibility,
|
||||
i0.Expression<String>? stackId,
|
||||
@@ -1547,6 +1610,7 @@ class RemoteAssetEntityCompanion
|
||||
if (localDateTime != null) 'local_date_time': localDateTime,
|
||||
if (thumbHash != null) 'thumb_hash': thumbHash,
|
||||
if (deletedAt != null) 'deleted_at': deletedAt,
|
||||
if (uploadedAt != null) 'uploaded_at': uploadedAt,
|
||||
if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId,
|
||||
if (visibility != null) 'visibility': visibility,
|
||||
if (stackId != null) 'stack_id': stackId,
|
||||
@@ -1570,6 +1634,7 @@ class RemoteAssetEntityCompanion
|
||||
i0.Value<DateTime?>? localDateTime,
|
||||
i0.Value<String?>? thumbHash,
|
||||
i0.Value<DateTime?>? deletedAt,
|
||||
i0.Value<DateTime?>? uploadedAt,
|
||||
i0.Value<String?>? livePhotoVideoId,
|
||||
i0.Value<i2.AssetVisibility>? visibility,
|
||||
i0.Value<String?>? stackId,
|
||||
@@ -1591,6 +1656,7 @@ class RemoteAssetEntityCompanion
|
||||
localDateTime: localDateTime ?? this.localDateTime,
|
||||
thumbHash: thumbHash ?? this.thumbHash,
|
||||
deletedAt: deletedAt ?? this.deletedAt,
|
||||
uploadedAt: uploadedAt ?? this.uploadedAt,
|
||||
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||
visibility: visibility ?? this.visibility,
|
||||
stackId: stackId ?? this.stackId,
|
||||
@@ -1646,6 +1712,9 @@ class RemoteAssetEntityCompanion
|
||||
if (deletedAt.present) {
|
||||
map['deleted_at'] = i0.Variable<DateTime>(deletedAt.value);
|
||||
}
|
||||
if (uploadedAt.present) {
|
||||
map['uploaded_at'] = i0.Variable<DateTime>(uploadedAt.value);
|
||||
}
|
||||
if (livePhotoVideoId.present) {
|
||||
map['live_photo_video_id'] = i0.Variable<String>(livePhotoVideoId.value);
|
||||
}
|
||||
@@ -1683,6 +1752,7 @@ class RemoteAssetEntityCompanion
|
||||
..write('localDateTime: $localDateTime, ')
|
||||
..write('thumbHash: $thumbHash, ')
|
||||
..write('deletedAt: $deletedAt, ')
|
||||
..write('uploadedAt: $uploadedAt, ')
|
||||
..write('livePhotoVideoId: $livePhotoVideoId, ')
|
||||
..write('visibility: $visibility, ')
|
||||
..write('stackId: $stackId, ')
|
||||
@@ -1693,10 +1763,6 @@ class RemoteAssetEntityCompanion
|
||||
}
|
||||
}
|
||||
|
||||
i0.Index get uQRemoteAssetsOwnerChecksum => i0.Index(
|
||||
'UQ_remote_assets_owner_checksum',
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)',
|
||||
);
|
||||
i0.Index get uQRemoteAssetsOwnerLibraryChecksum => i0.Index(
|
||||
'UQ_remote_assets_owner_library_checksum',
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)',
|
||||
@@ -1709,11 +1775,7 @@ i0.Index get idxRemoteAssetStackId => i0.Index(
|
||||
'idx_remote_asset_stack_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_stack_id ON remote_asset_entity (stack_id)',
|
||||
);
|
||||
i0.Index get idxRemoteAssetLocalDateTimeDay => i0.Index(
|
||||
'idx_remote_asset_local_date_time_day',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_day ON remote_asset_entity (STRFTIME(\'%Y-%m-%d\', local_date_time))',
|
||||
);
|
||||
i0.Index get idxRemoteAssetLocalDateTimeMonth => i0.Index(
|
||||
'idx_remote_asset_local_date_time_month',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_month ON remote_asset_entity (STRFTIME(\'%Y-%m\', local_date_time))',
|
||||
i0.Index get idxRemoteAssetOwnerVisibilityDeletedCreated => i0.Index(
|
||||
'idx_remote_asset_owner_visibility_deleted_created',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_visibility_deleted_created ON remote_asset_entity (owner_id, visibility, deleted_at, created_at DESC)',
|
||||
);
|
||||
|
||||
@@ -2,15 +2,15 @@ import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||
|
||||
part 'local_image_request.dart';
|
||||
part 'thumbhash_image_request.dart';
|
||||
part 'remote_image_request.dart';
|
||||
part 'thumbhash_image_request.dart';
|
||||
|
||||
abstract class ImageRequest {
|
||||
static int _nextRequestId = 0;
|
||||
@@ -74,7 +74,9 @@ abstract class ImageRequest {
|
||||
|
||||
Future<ui.FrameInfo?> _fromEncodedPlatformImage(int address, int length) async {
|
||||
final result = await _codecFromEncodedPlatformImage(address, length);
|
||||
if (result == null) return null;
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final (codec, descriptor) = result;
|
||||
if (_isCancelled) {
|
||||
|
||||
@@ -46,7 +46,9 @@ class LocalImageRequest extends ImageRequest {
|
||||
isVideo: assetType == AssetType.video,
|
||||
preferEncoded: true,
|
||||
);
|
||||
if (info == null) return null;
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final (codec, _) = await _codecFromEncodedPlatformImage(info['pointer']!, info['length']!) ?? (null, null);
|
||||
return codec;
|
||||
|
||||
@@ -29,7 +29,9 @@ class RemoteImageRequest extends ImageRequest {
|
||||
}
|
||||
|
||||
final info = await remoteImageApi.requestImage(uri, requestId: requestId, preferEncoded: true);
|
||||
if (info == null) return null;
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final (codec, _) = await _codecFromEncodedPlatformImage(info['pointer']!, info['length']!) ?? (null, null);
|
||||
return codec;
|
||||
|
||||
@@ -5,7 +5,9 @@ class ApiRepository {
|
||||
|
||||
Future<T> checkNull<T>(Future<T?> future) async {
|
||||
final response = await future;
|
||||
if (response == null) throw const NoResponseDtoError();
|
||||
if (response == null) {
|
||||
throw const NoResponseDtoError();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/memory.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/metadata.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/person.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
|
||||
@@ -29,6 +30,7 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.steps.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
@DriftDatabase(
|
||||
tables: [
|
||||
@@ -53,6 +55,7 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.steps.da
|
||||
StoreEntity,
|
||||
TrashedLocalAssetEntity,
|
||||
AssetEditEntity,
|
||||
MetadataEntity,
|
||||
],
|
||||
include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'},
|
||||
)
|
||||
@@ -83,8 +86,19 @@ class Drift extends $Drift {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> optimize({bool allTables = false}) async {
|
||||
try {
|
||||
if (allTables) {
|
||||
await customStatement('PRAGMA optimize=0x10002');
|
||||
}
|
||||
await customStatement('PRAGMA optimize');
|
||||
} catch (error) {
|
||||
Logger('Drift').fine('Failed to optimize database', error);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get schemaVersion => 24;
|
||||
int get schemaVersion => 26;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
@@ -250,6 +264,18 @@ class Drift extends $Drift {
|
||||
await customStatement('DROP INDEX IF EXISTS idx_remote_album_owner_id');
|
||||
await m.alterTable(TableMigration(v24.remoteAlbumEntity));
|
||||
},
|
||||
from24To25: (m, v25) async {
|
||||
await m.createTable(v25.metadata);
|
||||
await customStatement('DROP INDEX IF EXISTS idx_remote_asset_owner_checksum');
|
||||
await customStatement('DROP INDEX IF EXISTS idx_remote_asset_local_date_time_day');
|
||||
await customStatement('DROP INDEX IF EXISTS idx_remote_asset_local_date_time_month');
|
||||
await m.createIndex(v25.idxRemoteAssetOwnerVisibilityDeletedCreated);
|
||||
await m.createIndex(v25.idxRemoteExifCity);
|
||||
await m.createIndex(v25.idxAssetFaceVisiblePerson);
|
||||
},
|
||||
from25To26: (m, v26) async {
|
||||
await m.addColumn(v26.remoteAssetEntity, v26.remoteAssetEntity.uploadedAt);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -260,6 +286,7 @@ class Drift extends $Drift {
|
||||
}
|
||||
|
||||
await customStatement('PRAGMA foreign_keys = ON;');
|
||||
await optimize();
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
await customStatement('PRAGMA foreign_keys = ON');
|
||||
|
||||
+15
-7
@@ -43,9 +43,11 @@ import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity
|
||||
as i20;
|
||||
import 'package:immich_mobile/infrastructure/entities/asset_edit.entity.drift.dart'
|
||||
as i21;
|
||||
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
|
||||
import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart'
|
||||
as i22;
|
||||
import 'package:drift/internal/modular.dart' as i23;
|
||||
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
|
||||
as i23;
|
||||
import 'package:drift/internal/modular.dart' as i24;
|
||||
|
||||
abstract class $Drift extends i0.GeneratedDatabase {
|
||||
$Drift(i0.QueryExecutor e) : super(e);
|
||||
@@ -89,9 +91,12 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
||||
.$TrashedLocalAssetEntityTable(this);
|
||||
late final i21.$AssetEditEntityTable assetEditEntity = i21
|
||||
.$AssetEditEntityTable(this);
|
||||
i22.MergedAssetDrift get mergedAssetDrift => i23.ReadDatabaseContainer(
|
||||
late final i22.$MetadataEntityTable metadataEntity = i22.$MetadataEntityTable(
|
||||
this,
|
||||
).accessor<i22.MergedAssetDrift>(i22.MergedAssetDrift.new);
|
||||
);
|
||||
i23.MergedAssetDrift get mergedAssetDrift => i24.ReadDatabaseContainer(
|
||||
this,
|
||||
).accessor<i23.MergedAssetDrift>(i23.MergedAssetDrift.new);
|
||||
@override
|
||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||
@@ -108,13 +113,11 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
||||
i4.idxLocalAssetChecksum,
|
||||
i4.idxLocalAssetCloudId,
|
||||
i3.idxStackPrimaryAssetId,
|
||||
i2.idxRemoteAssetOwnerChecksum,
|
||||
i2.uQRemoteAssetsOwnerChecksum,
|
||||
i2.uQRemoteAssetsOwnerLibraryChecksum,
|
||||
i2.idxRemoteAssetChecksum,
|
||||
i2.idxRemoteAssetStackId,
|
||||
i2.idxRemoteAssetLocalDateTimeDay,
|
||||
i2.idxRemoteAssetLocalDateTimeMonth,
|
||||
i2.idxRemoteAssetOwnerVisibilityDeletedCreated,
|
||||
authUserEntity,
|
||||
userMetadataEntity,
|
||||
partnerEntity,
|
||||
@@ -129,13 +132,16 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
||||
storeEntity,
|
||||
trashedLocalAssetEntity,
|
||||
assetEditEntity,
|
||||
metadataEntity,
|
||||
i10.idxPartnerSharedWithId,
|
||||
i11.idxLatLng,
|
||||
i11.idxRemoteExifCity,
|
||||
i12.idxRemoteAlbumAssetAlbumAsset,
|
||||
i14.idxRemoteAssetCloudId,
|
||||
i17.idxPersonOwnerId,
|
||||
i18.idxAssetFacePersonId,
|
||||
i18.idxAssetFaceAssetId,
|
||||
i18.idxAssetFaceVisiblePerson,
|
||||
i20.idxTrashedLocalAssetChecksum,
|
||||
i20.idxTrashedLocalAssetAlbum,
|
||||
i21.idxAssetEditAssetId,
|
||||
@@ -389,4 +395,6 @@ class $DriftManager {
|
||||
);
|
||||
i21.$$AssetEditEntityTableTableManager get assetEditEntity =>
|
||||
i21.$$AssetEditEntityTableTableManager(_db, _db.assetEditEntity);
|
||||
i22.$$MetadataEntityTableTableManager get metadataEntity =>
|
||||
i22.$$MetadataEntityTableTableManager(_db, _db.metadataEntity);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,141 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/config/app_config.dart';
|
||||
import 'package:immich_mobile/domain/models/config/system_config.dart';
|
||||
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
|
||||
class MetadataRepository extends DriftDatabaseRepository {
|
||||
final Drift _db;
|
||||
final Map<MetadataKey, Object> _cache = {};
|
||||
|
||||
MetadataRepository._(this._db) : super(_db);
|
||||
|
||||
static MetadataRepository? _instance;
|
||||
|
||||
static MetadataRepository get instance {
|
||||
final instance = _instance;
|
||||
if (instance == null) {
|
||||
throw StateError('MetadataRepository not initialized. Call ensureInitialized() first');
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
AppConfig _appConfig = const .new();
|
||||
AppConfig get appConfig => _appConfig;
|
||||
|
||||
SystemConfig _systemConfig = const .new();
|
||||
SystemConfig get systemConfig => _systemConfig;
|
||||
|
||||
static Future<MetadataRepository> ensureInitialized(Drift db) async {
|
||||
if (_instance == null) {
|
||||
final instance = MetadataRepository._(db);
|
||||
await instance._hydrate();
|
||||
_instance = instance;
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static Future<void> refresh() async {
|
||||
instance._cache.clear();
|
||||
instance._appConfig = const .new();
|
||||
instance._systemConfig = const .new();
|
||||
await instance._hydrate();
|
||||
}
|
||||
|
||||
Future<void> _hydrate() async => _hydrateCache(await _db.select(_db.metadataEntity).get());
|
||||
|
||||
T _read<T extends Object>(MetadataKey<T> key) => (_cache[key] as T?) ?? key.defaultValue;
|
||||
|
||||
Future<void> write<T extends Object, U extends T>(MetadataKey<T> key, U value) async {
|
||||
if (_read(key) == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
await _db
|
||||
.into(_db.metadataEntity)
|
||||
.insertOnConflictUpdate(
|
||||
MetadataEntityCompanion.insert(key: key.key, value: key.encode(value), updatedAt: Value(DateTime.now())),
|
||||
);
|
||||
_updateCache(key, value);
|
||||
}
|
||||
|
||||
Future<void> delete<T extends Object>(MetadataKey<T> key) async {
|
||||
await (_db.delete(_db.metadataEntity)..where((t) => t.key.equals(key.key))).go();
|
||||
_updateCache(key, key.defaultValue);
|
||||
}
|
||||
|
||||
Stream<AppConfig> watchAppConfig() => _watchDomain(.appConfig).distinct();
|
||||
|
||||
Stream<SystemConfig> watchSystemConfig() => _watchDomain(.systemConfig).distinct();
|
||||
|
||||
Stream<T> _watchDomain<T extends Object>(MetadataDomain<T> domain) {
|
||||
final query = _db.select(_db.metadataEntity)..where((t) => t.key.like('${domain.prefix}.%'));
|
||||
return query.watch().map((rows) {
|
||||
_hydrateCache(rows);
|
||||
return domain.config(this);
|
||||
});
|
||||
}
|
||||
|
||||
void _hydrateCache(List<MetadataEntityData> rows) {
|
||||
final keyMap = MetadataKey.asKeyMap();
|
||||
for (final row in rows) {
|
||||
final key = keyMap[row.key];
|
||||
if (key == null) {
|
||||
continue;
|
||||
}
|
||||
_updateCache(key, key.decode(row.value));
|
||||
}
|
||||
}
|
||||
|
||||
void _updateCache<T extends Object>(MetadataKey<T> key, T value) {
|
||||
if (_cache[key] == value) {
|
||||
return;
|
||||
}
|
||||
_cache[key] = value;
|
||||
key.domain.rebuild(this);
|
||||
}
|
||||
}
|
||||
|
||||
extension<T extends Object> on MetadataDomain<T> {
|
||||
T config(MetadataRepository repo) => switch (this) {
|
||||
.appConfig => repo._appConfig as T,
|
||||
.systemConfig => repo._systemConfig as T,
|
||||
};
|
||||
|
||||
void rebuild(MetadataRepository repo) {
|
||||
switch (this) {
|
||||
case .appConfig:
|
||||
repo._appConfig = .new(
|
||||
theme: .new(
|
||||
mode: repo._read(.themeMode),
|
||||
primaryColor: repo._read(.themePrimaryColor),
|
||||
dynamicTheme: repo._read(.themeDynamic),
|
||||
colorfulInterface: repo._read(.themeColorfulInterface),
|
||||
),
|
||||
cleanup: .new(
|
||||
keepFavorites: repo._read(.cleanupKeepFavorites),
|
||||
keepMediaType: repo._read(.cleanupKeepMediaType),
|
||||
keepAlbumIds: repo._read(.cleanupKeepAlbumIds),
|
||||
cutoffDaysAgo: repo._read(.cleanupCutoffDaysAgo),
|
||||
defaultsInitialized: repo._read(.cleanupDefaultsInitialized),
|
||||
),
|
||||
map: .new(
|
||||
relativeDays: repo._read(.mapRelativeDate),
|
||||
favoritesOnly: repo._read(.mapShowFavoriteOnly),
|
||||
includeArchived: repo._read(.mapIncludeArchived),
|
||||
themeMode: repo._read(.mapThemeMode),
|
||||
withPartners: repo._read(.mapWithPartners),
|
||||
),
|
||||
timeline: .new(
|
||||
tilesPerRow: repo._read(.timelineTilesPerRow),
|
||||
groupAssetsBy: repo._read(.timelineGroupAssetsBy),
|
||||
storageIndicator: repo._read(.timelineStorageIndicator),
|
||||
),
|
||||
image: .new(preferRemote: repo._read(.imagePreferRemote), loadOriginal: repo._read(.imageLoadOriginal)),
|
||||
);
|
||||
case .systemConfig:
|
||||
repo._systemConfig = .new(logLevel: repo._read(.logLevel));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,7 +392,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
|
||||
Future<List<String>> getSortedAlbumIds(List<String> albumIds, {required AssetDateAggregation aggregation}) async {
|
||||
if (albumIds.isEmpty) return [];
|
||||
if (albumIds.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final jsonIds = jsonEncode(albumIds);
|
||||
final sqlAgg = aggregation == AssetDateAggregation.start ? 'MIN' : 'MAX';
|
||||
|
||||
@@ -164,6 +164,16 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> emptyTrash(String ownerId) async {
|
||||
await _db.remoteAssetEntity.deleteWhere((t) => t.deletedAt.isNotNull() & t.ownerId.equals(ownerId));
|
||||
}
|
||||
|
||||
Future<void> restoreAllTrash(String ownerId) async {
|
||||
await (_db.remoteAssetEntity.update()..where((t) => t.deletedAt.isNotNull() & t.ownerId.equals(ownerId))).write(
|
||||
const RemoteAssetEntityCompanion(deletedAt: Value(null)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> delete(List<String> ids) {
|
||||
return _db.batch((batch) {
|
||||
for (final id in ids) {
|
||||
|
||||
@@ -3,9 +3,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/semver.dart';
|
||||
@@ -38,7 +36,6 @@ class SyncApiRepository {
|
||||
|
||||
final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'};
|
||||
|
||||
final shouldReset = Store.get(StoreKey.shouldResetSync, false);
|
||||
final request = http.Request('POST', Uri.parse(endpoint));
|
||||
request.headers.addAll(headers);
|
||||
request.body = jsonEncode(
|
||||
@@ -46,19 +43,25 @@ class SyncApiRepository {
|
||||
types: [
|
||||
SyncRequestType.authUsersV1,
|
||||
SyncRequestType.usersV1,
|
||||
SyncRequestType.assetsV1,
|
||||
serverVersion >= const SemVer(major: 3, minor: 0, patch: 0)
|
||||
? SyncRequestType.assetsV2
|
||||
: SyncRequestType.assetsV1,
|
||||
SyncRequestType.assetExifsV1,
|
||||
if (serverVersion >= const SemVer(major: 2, minor: 6, patch: 0)) SyncRequestType.assetEditsV1,
|
||||
SyncRequestType.assetMetadataV1,
|
||||
SyncRequestType.partnersV1,
|
||||
SyncRequestType.partnerAssetsV1,
|
||||
serverVersion >= const SemVer(major: 3, minor: 0, patch: 0)
|
||||
? SyncRequestType.partnerAssetsV2
|
||||
: SyncRequestType.partnerAssetsV1,
|
||||
SyncRequestType.partnerAssetExifsV1,
|
||||
if (serverVersion < const SemVer(major: 3, minor: 0, patch: 0))
|
||||
SyncRequestType.albumsV1
|
||||
else
|
||||
SyncRequestType.albumsV2,
|
||||
SyncRequestType.albumUsersV1,
|
||||
SyncRequestType.albumAssetsV1,
|
||||
serverVersion >= const SemVer(major: 3, minor: 0, patch: 0)
|
||||
? SyncRequestType.albumAssetsV2
|
||||
: SyncRequestType.albumAssetsV1,
|
||||
SyncRequestType.albumAssetExifsV1,
|
||||
SyncRequestType.albumToAssetsV1,
|
||||
SyncRequestType.memoriesV1,
|
||||
@@ -67,10 +70,10 @@ class SyncApiRepository {
|
||||
SyncRequestType.partnerStacksV1,
|
||||
SyncRequestType.userMetadataV1,
|
||||
SyncRequestType.peopleV1,
|
||||
if (serverVersion < const SemVer(major: 2, minor: 6, patch: 0)) SyncRequestType.assetFacesV1,
|
||||
if (serverVersion >= const SemVer(major: 2, minor: 6, patch: 0)) SyncRequestType.assetFacesV2,
|
||||
serverVersion >= const SemVer(major: 2, minor: 6, patch: 0)
|
||||
? SyncRequestType.assetFacesV2
|
||||
: SyncRequestType.assetFacesV1,
|
||||
],
|
||||
reset: shouldReset,
|
||||
).toJson(),
|
||||
);
|
||||
|
||||
@@ -94,9 +97,6 @@ class SyncApiRepository {
|
||||
throw ApiException(response.statusCode, 'Failed to get sync stream: $errorBody');
|
||||
}
|
||||
|
||||
// Reset after successful stream start
|
||||
await Store.put(StoreKey.shouldResetSync, false);
|
||||
|
||||
await for (final chunk in response.stream.transform(utf8.decoder)) {
|
||||
if (shouldAbort) {
|
||||
break;
|
||||
@@ -153,6 +153,7 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
||||
SyncEntityType.partnerV1: SyncPartnerV1.fromJson,
|
||||
SyncEntityType.partnerDeleteV1: SyncPartnerDeleteV1.fromJson,
|
||||
SyncEntityType.assetV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.assetV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.assetDeleteV1: SyncAssetDeleteV1.fromJson,
|
||||
SyncEntityType.assetExifV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.assetEditV1: SyncAssetEditV1.fromJson,
|
||||
@@ -160,7 +161,9 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
||||
SyncEntityType.assetMetadataV1: SyncAssetMetadataV1.fromJson,
|
||||
SyncEntityType.assetMetadataDeleteV1: SyncAssetMetadataDeleteV1.fromJson,
|
||||
SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.partnerAssetV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.partnerAssetBackfillV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.partnerAssetBackfillV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
|
||||
SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.partnerAssetExifBackfillV1: SyncAssetExifV1.fromJson,
|
||||
@@ -171,8 +174,11 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
||||
SyncEntityType.albumUserBackfillV1: SyncAlbumUserV1.fromJson,
|
||||
SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson,
|
||||
SyncEntityType.albumAssetCreateV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.albumAssetCreateV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.albumAssetUpdateV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.albumAssetUpdateV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.albumAssetBackfillV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.albumAssetBackfillV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.albumAssetExifCreateV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.albumAssetExifUpdateV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.albumAssetExifBackfillV1: SyncAssetExifV1.fromJson,
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:immich_mobile/infrastructure/entities/asset_edit.entity.drift.da
|
||||
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
||||
@@ -45,25 +46,35 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
// foreign_keys PRAGMA is no-op within transactions
|
||||
// https://www.sqlite.org/pragma.html#pragma_foreign_keys
|
||||
await _db.customStatement('PRAGMA foreign_keys = OFF');
|
||||
await transaction(() async {
|
||||
await _db.assetFaceEntity.deleteAll();
|
||||
await _db.memoryAssetEntity.deleteAll();
|
||||
await _db.memoryEntity.deleteAll();
|
||||
await _db.partnerEntity.deleteAll();
|
||||
await _db.personEntity.deleteAll();
|
||||
await _db.remoteAlbumAssetEntity.deleteAll();
|
||||
await _db.remoteAlbumEntity.deleteAll();
|
||||
await _db.remoteAlbumUserEntity.deleteAll();
|
||||
await _db.remoteAssetEntity.deleteAll();
|
||||
await _db.remoteExifEntity.deleteAll();
|
||||
await _db.stackEntity.deleteAll();
|
||||
await _db.authUserEntity.deleteAll();
|
||||
await _db.userEntity.deleteAll();
|
||||
await _db.userMetadataEntity.deleteAll();
|
||||
await _db.remoteAssetCloudIdEntity.deleteAll();
|
||||
await _db.assetEditEntity.deleteAll();
|
||||
});
|
||||
await _db.customStatement('PRAGMA foreign_keys = ON');
|
||||
try {
|
||||
await transaction(() async {
|
||||
// FK cascade (ON DELETE SET NULL) does not fire while foreign_keys = OFF,
|
||||
// so null linkedRemoteAlbumId manually to avoid dangling pointers in local_album_entity.
|
||||
await _db.localAlbumEntity.update().write(
|
||||
const LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(null)),
|
||||
);
|
||||
await _db.assetFaceEntity.deleteAll();
|
||||
await _db.memoryAssetEntity.deleteAll();
|
||||
await _db.memoryEntity.deleteAll();
|
||||
await _db.partnerEntity.deleteAll();
|
||||
await _db.personEntity.deleteAll();
|
||||
await _db.remoteAlbumAssetEntity.deleteAll();
|
||||
await _db.remoteAlbumEntity.deleteAll();
|
||||
await _db.remoteAlbumUserEntity.deleteAll();
|
||||
await _db.remoteAssetEntity.deleteAll();
|
||||
await _db.remoteExifEntity.deleteAll();
|
||||
await _db.stackEntity.deleteAll();
|
||||
await _db.authUserEntity.deleteAll();
|
||||
await _db.userEntity.deleteAll();
|
||||
await _db.userMetadataEntity.deleteAll();
|
||||
await _db.remoteAssetCloudIdEntity.deleteAll();
|
||||
await _db.assetEditEntity.deleteAll();
|
||||
});
|
||||
} finally {
|
||||
// re-enable FK even if the transaction throws, otherwise the connection
|
||||
// would be left with foreign_keys = OFF, silently disabling cascades.
|
||||
await _db.customStatement('PRAGMA foreign_keys = ON');
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: SyncResetV1', error, stack);
|
||||
@@ -191,6 +202,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
type: Value(asset.type.toAssetType()),
|
||||
createdAt: Value.absentIfNull(asset.fileCreatedAt),
|
||||
updatedAt: Value.absentIfNull(asset.fileModifiedAt),
|
||||
uploadedAt: Value(asset.createdAt),
|
||||
durationMs: Value(asset.duration?.toDuration()?.inMilliseconds ?? 0),
|
||||
checksum: Value(asset.checksum),
|
||||
isFavorite: Value(asset.isFavorite),
|
||||
@@ -220,6 +232,45 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateAssetsV2(Iterable<SyncAssetV2> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final asset in data) {
|
||||
final companion = RemoteAssetEntityCompanion(
|
||||
name: Value(asset.originalFileName),
|
||||
type: Value(asset.type.toAssetType()),
|
||||
createdAt: Value.absentIfNull(asset.fileCreatedAt),
|
||||
updatedAt: Value.absentIfNull(asset.fileModifiedAt),
|
||||
uploadedAt: Value(asset.createdAt),
|
||||
durationMs: Value(asset.duration),
|
||||
checksum: Value(asset.checksum),
|
||||
isFavorite: Value(asset.isFavorite),
|
||||
ownerId: Value(asset.ownerId),
|
||||
localDateTime: Value(asset.localDateTime),
|
||||
thumbHash: Value(asset.thumbhash),
|
||||
deletedAt: Value(asset.deletedAt),
|
||||
visibility: Value(asset.visibility.toAssetVisibility()),
|
||||
livePhotoVideoId: Value(asset.livePhotoVideoId),
|
||||
stackId: Value(asset.stackId),
|
||||
libraryId: Value(asset.libraryId),
|
||||
width: Value(asset.width),
|
||||
height: Value(asset.height),
|
||||
isEdited: Value(asset.isEdited),
|
||||
);
|
||||
|
||||
batch.insert(
|
||||
_db.remoteAssetEntity,
|
||||
companion.copyWith(id: Value(asset.id)),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: updateAssetsV2 - $debugLabel', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
|
||||
@@ -79,6 +79,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
type: row.type,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
uploadedAt: row.uploadedAt,
|
||||
thumbHash: row.thumbHash,
|
||||
width: row.width,
|
||||
height: row.height,
|
||||
@@ -244,17 +245,21 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
|
||||
final isAscending = albumData.order == AlbumAssetOrder.asc;
|
||||
|
||||
final query = _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([
|
||||
// Correlated subquery picks the first matching local asset by checksum,
|
||||
// avoiding fan-out when the same photo exists in multiple device albums (#23273).
|
||||
final localId = subqueryExpression<String>(
|
||||
_db.localAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAssetEntity.id])
|
||||
..where(_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum))
|
||||
..limit(1),
|
||||
);
|
||||
|
||||
final query = _db.remoteAssetEntity.select().addColumns([localId]).join([
|
||||
innerJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId));
|
||||
|
||||
if (isAscending) {
|
||||
@@ -265,9 +270,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
|
||||
query.limit(count, offset: offset);
|
||||
|
||||
return query
|
||||
.map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id)))
|
||||
.get();
|
||||
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(localId))).get();
|
||||
}
|
||||
|
||||
TimelineQuery fromAssets(List<BaseAsset> assets, TimelineOrigin origin) => (
|
||||
@@ -315,6 +318,17 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
origin: TimelineOrigin.remoteAssets,
|
||||
);
|
||||
|
||||
TimelineQuery recentlyAdded(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
|
||||
filter: (row) =>
|
||||
row.uploadedAt.isNotNull() &
|
||||
row.deletedAt.isNull() &
|
||||
row.ownerId.equals(userId) &
|
||||
(row.visibility.equalsValue(AssetVisibility.timeline) | row.visibility.equalsValue(AssetVisibility.archive)),
|
||||
origin: TimelineOrigin.recentlyAdded,
|
||||
groupBy: groupBy,
|
||||
sortBy: SortAssetsBy.uploaded,
|
||||
);
|
||||
|
||||
TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() &
|
||||
@@ -422,23 +436,22 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
|
||||
Stream<List<Bucket>> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
|
||||
final idQuery = _db.assetFaceEntity.selectOnly()
|
||||
..addColumns([_db.assetFaceEntity.assetId])
|
||||
..where(
|
||||
_db.assetFaceEntity.personId.equals(personId) &
|
||||
_db.assetFaceEntity.isVisible.equals(true) &
|
||||
_db.assetFaceEntity.deletedAt.isNull(),
|
||||
);
|
||||
|
||||
if (groupBy == GroupAssetsBy.none) {
|
||||
final query = _db.remoteAssetEntity.selectOnly()
|
||||
..addColumns([_db.remoteAssetEntity.id.count()])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.assetFaceEntity,
|
||||
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.id.isInQuery(idQuery) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
||||
_db.assetFaceEntity.personId.equals(personId) &
|
||||
_db.assetFaceEntity.isVisible.equals(true) &
|
||||
_db.assetFaceEntity.deletedAt.isNull(),
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
|
||||
);
|
||||
|
||||
return query.map((row) {
|
||||
@@ -452,20 +465,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
|
||||
final query = _db.remoteAssetEntity.selectOnly()
|
||||
..addColumns([assetCountExp, dateExp])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.assetFaceEntity,
|
||||
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.id.isInQuery(idQuery) &
|
||||
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
||||
_db.assetFaceEntity.personId.equals(personId) &
|
||||
_db.assetFaceEntity.isVisible.equals(true) &
|
||||
_db.assetFaceEntity.deletedAt.isNull(),
|
||||
_db.remoteAssetEntity.deletedAt.isNull(),
|
||||
)
|
||||
..groupBy([dateExp])
|
||||
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||
@@ -483,26 +487,26 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
required int offset,
|
||||
required int count,
|
||||
}) {
|
||||
final query =
|
||||
_db.remoteAssetEntity.select().join([
|
||||
innerJoin(
|
||||
_db.assetFaceEntity,
|
||||
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
||||
_db.assetFaceEntity.personId.equals(personId) &
|
||||
_db.assetFaceEntity.isVisible.equals(true) &
|
||||
_db.assetFaceEntity.deletedAt.isNull(),
|
||||
)
|
||||
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
final idQuery = _db.assetFaceEntity.selectOnly()
|
||||
..addColumns([_db.assetFaceEntity.assetId])
|
||||
..where(
|
||||
_db.assetFaceEntity.personId.equals(personId) &
|
||||
_db.assetFaceEntity.isVisible.equals(true) &
|
||||
_db.assetFaceEntity.deletedAt.isNull(),
|
||||
);
|
||||
|
||||
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
|
||||
final query = _db.remoteAssetEntity.select()
|
||||
..where(
|
||||
(row) =>
|
||||
row.id.isInQuery(idQuery) &
|
||||
row.deletedAt.isNull() &
|
||||
row.ownerId.equals(userId) &
|
||||
row.visibility.equalsValue(AssetVisibility.timeline),
|
||||
)
|
||||
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
|
||||
return query.map((row) => row.toDto()).get();
|
||||
}
|
||||
|
||||
TimelineQuery map(List<String> userIds, TimelineMapOptions options, GroupAssetsBy groupBy) => (
|
||||
@@ -605,9 +609,10 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
required TimelineOrigin origin,
|
||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||
bool joinLocal = false,
|
||||
SortAssetsBy sortBy = SortAssetsBy.taken,
|
||||
}) {
|
||||
return (
|
||||
bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy),
|
||||
bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy, sortBy: sortBy),
|
||||
assetSource: (offset, count) =>
|
||||
_getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal),
|
||||
origin: origin,
|
||||
@@ -617,6 +622,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
Stream<List<Bucket>> _watchRemoteBucket({
|
||||
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
|
||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||
SortAssetsBy sortBy = SortAssetsBy.taken,
|
||||
}) {
|
||||
if (groupBy == GroupAssetsBy.none) {
|
||||
final query = _db.remoteAssetEntity.count(where: filter);
|
||||
@@ -624,7 +630,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
|
||||
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||
final dateExp = _db.remoteAssetEntity.effectiveCreatedAt(groupBy);
|
||||
final dateExp = _db.remoteAssetEntity.effectiveCreatedAt(groupBy, sortBy: sortBy);
|
||||
|
||||
final query = _db.remoteAssetEntity.selectOnly()
|
||||
..addColumns([assetCountExp, dateExp])
|
||||
@@ -700,8 +706,13 @@ extension on Expression<DateTime> {
|
||||
}
|
||||
|
||||
extension on $RemoteAssetEntityTable {
|
||||
Expression<String> effectiveCreatedAt(GroupAssetsBy groupBy) =>
|
||||
coalesce([localDateTime.dateFmt(groupBy), createdAt.dateFmt(groupBy, toLocal: true)]);
|
||||
Expression<String> effectiveCreatedAt(GroupAssetsBy groupBy, {SortAssetsBy sortBy = SortAssetsBy.taken}) {
|
||||
if (sortBy == SortAssetsBy.uploaded) {
|
||||
return uploadedAt.dateFmt(groupBy, toLocal: true);
|
||||
}
|
||||
|
||||
return coalesce([localDateTime.dateFmt(groupBy), createdAt.dateFmt(groupBy, toLocal: true)]);
|
||||
}
|
||||
}
|
||||
|
||||
extension on String {
|
||||
|
||||
@@ -12,7 +12,9 @@ class DriftAuthUserRepository extends DriftDatabaseRepository {
|
||||
Future<UserDto?> get(String id) async {
|
||||
final user = await _db.managers.authUserEntity.filter((user) => user.id.equals(id)).getSingleOrNull();
|
||||
|
||||
if (user == null) return null;
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(id));
|
||||
final metadata = await query.map((row) => row.toDto()).get();
|
||||
|
||||
@@ -12,7 +12,9 @@ class UserApiRepository extends ApiRepository {
|
||||
|
||||
Future<UserDto?> getMyUser() async {
|
||||
final (adminDto, preferenceDto) = await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
||||
if (adminDto == null) return null;
|
||||
if (adminDto == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user