more changes

This commit is contained in:
shenlong-tanwen
2025-12-11 16:38:39 -06:00
parent 04dbd8b3b1
commit e75d79bee3
14 changed files with 499 additions and 161 deletions

File diff suppressed because one or more lines are too long

View File

@@ -47,7 +47,6 @@ const List<(String, String)> kWidgetNames = [
const double kUploadStatusFailed = -1.0;
const double kUploadStatusCanceled = -2.0;
const String kUploadETagDelimiter = "_";
const int kMinMonthsToEnableScrubberSnap = 12;

View File

@@ -25,9 +25,18 @@ class RemoteAssetMetadataItem {
class RemoteAssetMobileAppMetadata extends RemoteAssetMetadataValue {
final String? cloudId;
final String? eTag;
final String? createdAt;
final String? adjustmentTime;
final String? latitude;
final String? longitude;
const RemoteAssetMobileAppMetadata({this.cloudId, this.eTag});
const RemoteAssetMobileAppMetadata({
this.cloudId,
this.createdAt,
this.adjustmentTime,
this.latitude,
this.longitude,
});
@override
Map<String, dynamic> toJson() {
@@ -35,8 +44,17 @@ class RemoteAssetMobileAppMetadata extends RemoteAssetMetadataValue {
if (cloudId != null) {
map["iCloudId"] = cloudId;
}
if (eTag != null) {
map["eTag"] = eTag;
if (createdAt != null) {
map["createdAt"] = createdAt;
}
if (adjustmentTime != null) {
map["adjustmentTime"] = adjustmentTime;
}
if (latitude != null) {
map["latitude"] = latitude;
}
if (longitude != null) {
map["longitude"] = longitude;
}
return map;

View File

@@ -1,6 +1,3 @@
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/extensions/num_extensions.dart';
part 'local_asset.model.dart';
part 'remote_asset.model.dart';

View File

@@ -42,14 +42,6 @@ class LocalAsset extends BaseAsset {
@override
String get heroTag => '${id}_${remoteId ?? checksum}';
String get eTag {
final createdAt = this.createdAt.millisecondsSinceEpoch ~/ 1000;
final adjustmentTime = this.adjustmentTime?.millisecondsSinceEpoch ?? 0;
final latitude = this.latitude?.truncateTo(2).toStringAsFixed(2) ?? "0.00";
final longitude = this.longitude?.truncateTo(2).toStringAsFixed(2) ?? "0.00";
return "$createdAt$kUploadETagDelimiter$adjustmentTime$kUploadETagDelimiter$latitude$kUploadETagDelimiter$longitude";
}
bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0;
@override

View File

@@ -1,9 +1,7 @@
import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/drift_extensions.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
@@ -36,7 +34,13 @@ Future<void> syncCloudIds(ProviderContainer ref) async {
for (final mapping in mappingsToUpdate) {
final mobileMeta = AssetMetadataUpsertItemDto(
key: AssetMetadataKey.mobileApp,
value: RemoteAssetMobileAppMetadata(cloudId: mapping.localAsset.cloudId, eTag: mapping.localAsset.eTag),
value: RemoteAssetMobileAppMetadata(
cloudId: mapping.localAsset.cloudId,
createdAt: mapping.localAsset.createdAt.toIso8601String(),
adjustmentTime: mapping.localAsset.adjustmentTime?.toIso8601String(),
latitude: mapping.localAsset.latitude?.toString(),
longitude: mapping.localAsset.longitude?.toString(),
),
);
try {
await assetApi.updateAssetMetadata(mapping.remoteAssetId, AssetMetadataUpsertDto(items: [mobileMeta]));
@@ -58,20 +62,6 @@ Future<void> _populateCloudIds(Drift drift) async {
typedef _CloudIdMapping = ({String remoteAssetId, LocalAsset localAsset});
Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId) async {
final createdAt = drift.localAssetEntity.createdAt.strftime('%s');
final adjustmentTime = coalesce([drift.localAssetEntity.adjustmentTime.strftime('%s'), const Constant('0')]);
final latitude = coalesce([
drift.localAssetEntity.latitude,
const Constant(0.0),
]).truncateTo(2).cast(DriftSqlType.string);
final longitude = coalesce([
drift.localAssetEntity.longitude,
const Constant(0.0),
]).truncateTo(2).cast(DriftSqlType.string);
final delimiter = const Constant(kUploadETagDelimiter);
final eTag = createdAt + delimiter + adjustmentTime + delimiter + latitude + delimiter + longitude;
final query =
drift.remoteAssetEntity.select().join([
leftOuterJoin(
@@ -80,7 +70,7 @@ Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId)
),
leftOuterJoin(
drift.remoteAssetCloudIdEntity,
drift.localAssetEntity.iCloudId.equalsExp(drift.remoteAssetCloudIdEntity.cloudId),
drift.remoteAssetEntity.id.equalsExp(drift.remoteAssetCloudIdEntity.assetId),
useColumns: false,
),
])..where(
@@ -88,7 +78,11 @@ Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId)
drift.localAssetEntity.id.isNotNull() &
drift.localAssetEntity.iCloudId.isNotNull() &
drift.remoteAssetEntity.ownerId.equals(userId) &
(drift.remoteAssetCloudIdEntity.cloudId.isNull() | drift.remoteAssetCloudIdEntity.eTag.isNotExp(eTag)),
(drift.remoteAssetCloudIdEntity.cloudId.isNull() |
((drift.remoteAssetCloudIdEntity.adjustmentTime.isNotExp(drift.localAssetEntity.adjustmentTime)) &
(drift.remoteAssetCloudIdEntity.latitude.isNotExp(drift.localAssetEntity.latitude)) &
(drift.remoteAssetCloudIdEntity.longitude.isNotExp(drift.localAssetEntity.longitude)) &
(drift.remoteAssetCloudIdEntity.createdAt.isNotExp(drift.localAssetEntity.createdAt)))),
);
return query.map((row) {
return (

View File

@@ -1,17 +0,0 @@
import 'dart:math';
import 'package:drift/drift.dart';
// ignore: invalid_use_of_internal_member, implementation_imports
import 'package:drift/src/runtime/query_builder/expressions/internal.dart';
extension DoubleTruncateExpression<T extends num> on Expression<T> {
Expression<T> truncateTo(int fractionDigits) {
final mod = Constant(pow(10, fractionDigits).toDouble());
return BaseInfixOperator(
BaseInfixOperator(this, '*', mod, precedence: Precedence.mulDivide).cast(DriftSqlType.int),
'/',
mod,
precedence: Precedence.mulDivide,
);
}
}

View File

@@ -7,7 +7,13 @@ class RemoteAssetCloudIdEntity extends Table with DriftDefaultsMixin {
TextColumn get cloudId => text().unique().nullable()();
TextColumn get eTag => text().nullable()();
DateTimeColumn get createdAt => dateTime().nullable()();
DateTimeColumn get adjustmentTime => dateTime().nullable()();
RealColumn get latitude => real().nullable()();
RealColumn get longitude => real().nullable()();
@override
Set<Column> get primaryKey => {assetId};

View File

@@ -13,13 +13,19 @@ typedef $$RemoteAssetCloudIdEntityTableCreateCompanionBuilder =
i1.RemoteAssetCloudIdEntityCompanion Function({
required String assetId,
i0.Value<String?> cloudId,
i0.Value<String?> eTag,
i0.Value<DateTime?> createdAt,
i0.Value<DateTime?> adjustmentTime,
i0.Value<double?> latitude,
i0.Value<double?> longitude,
});
typedef $$RemoteAssetCloudIdEntityTableUpdateCompanionBuilder =
i1.RemoteAssetCloudIdEntityCompanion Function({
i0.Value<String> assetId,
i0.Value<String?> cloudId,
i0.Value<String?> eTag,
i0.Value<DateTime?> createdAt,
i0.Value<DateTime?> adjustmentTime,
i0.Value<double?> latitude,
i0.Value<double?> longitude,
});
final class $$RemoteAssetCloudIdEntityTableReferences
@@ -85,8 +91,23 @@ class $$RemoteAssetCloudIdEntityTableFilterComposer
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get eTag => $composableBuilder(
column: $table.eTag,
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get adjustmentTime => $composableBuilder(
column: $table.adjustmentTime,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<double> get latitude => $composableBuilder(
column: $table.latitude,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<double> get longitude => $composableBuilder(
column: $table.longitude,
builder: (column) => i0.ColumnFilters(column),
);
@@ -133,8 +154,23 @@ class $$RemoteAssetCloudIdEntityTableOrderingComposer
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get eTag => $composableBuilder(
column: $table.eTag,
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get adjustmentTime => $composableBuilder(
column: $table.adjustmentTime,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<double> get latitude => $composableBuilder(
column: $table.latitude,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<double> get longitude => $composableBuilder(
column: $table.longitude,
builder: (column) => i0.ColumnOrderings(column),
);
@@ -180,8 +216,19 @@ class $$RemoteAssetCloudIdEntityTableAnnotationComposer
i0.GeneratedColumn<String> get cloudId =>
$composableBuilder(column: $table.cloudId, builder: (column) => column);
i0.GeneratedColumn<String> get eTag =>
$composableBuilder(column: $table.eTag, builder: (column) => column);
i0.GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
i0.GeneratedColumn<DateTime> get adjustmentTime => $composableBuilder(
column: $table.adjustmentTime,
builder: (column) => column,
);
i0.GeneratedColumn<double> get latitude =>
$composableBuilder(column: $table.latitude, builder: (column) => column);
i0.GeneratedColumn<double> get longitude =>
$composableBuilder(column: $table.longitude, builder: (column) => column);
i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
@@ -256,21 +303,33 @@ class $$RemoteAssetCloudIdEntityTableTableManager
({
i0.Value<String> assetId = const i0.Value.absent(),
i0.Value<String?> cloudId = const i0.Value.absent(),
i0.Value<String?> eTag = const i0.Value.absent(),
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
i0.Value<double?> latitude = const i0.Value.absent(),
i0.Value<double?> longitude = const i0.Value.absent(),
}) => i1.RemoteAssetCloudIdEntityCompanion(
assetId: assetId,
cloudId: cloudId,
eTag: eTag,
createdAt: createdAt,
adjustmentTime: adjustmentTime,
latitude: latitude,
longitude: longitude,
),
createCompanionCallback:
({
required String assetId,
i0.Value<String?> cloudId = const i0.Value.absent(),
i0.Value<String?> eTag = const i0.Value.absent(),
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
i0.Value<double?> latitude = const i0.Value.absent(),
i0.Value<double?> longitude = const i0.Value.absent(),
}) => i1.RemoteAssetCloudIdEntityCompanion.insert(
assetId: assetId,
cloudId: cloudId,
eTag: eTag,
createdAt: createdAt,
adjustmentTime: adjustmentTime,
latitude: latitude,
longitude: longitude,
),
withReferenceMapper: (p0) => p0
.map(
@@ -381,19 +440,60 @@ class $RemoteAssetCloudIdEntityTable extends i2.RemoteAssetCloudIdEntity
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways('UNIQUE'),
);
static const i0.VerificationMeta _eTagMeta = const i0.VerificationMeta(
'eTag',
static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta(
'createdAt',
);
@override
late final i0.GeneratedColumn<String> eTag = i0.GeneratedColumn<String>(
'e_tag',
late final i0.GeneratedColumn<DateTime> createdAt =
i0.GeneratedColumn<DateTime>(
'created_at',
aliasedName,
true,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _adjustmentTimeMeta =
const i0.VerificationMeta('adjustmentTime');
@override
late final i0.GeneratedColumn<DateTime> adjustmentTime =
i0.GeneratedColumn<DateTime>(
'adjustment_time',
aliasedName,
true,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _latitudeMeta = const i0.VerificationMeta(
'latitude',
);
@override
late final i0.GeneratedColumn<double> latitude = i0.GeneratedColumn<double>(
'latitude',
aliasedName,
true,
type: i0.DriftSqlType.string,
type: i0.DriftSqlType.double,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _longitudeMeta = const i0.VerificationMeta(
'longitude',
);
@override
late final i0.GeneratedColumn<double> longitude = i0.GeneratedColumn<double>(
'longitude',
aliasedName,
true,
type: i0.DriftSqlType.double,
requiredDuringInsert: false,
);
@override
List<i0.GeneratedColumn> get $columns => [assetId, cloudId, eTag];
List<i0.GeneratedColumn> get $columns => [
assetId,
cloudId,
createdAt,
adjustmentTime,
latitude,
longitude,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
@@ -420,10 +520,31 @@ class $RemoteAssetCloudIdEntityTable extends i2.RemoteAssetCloudIdEntity
cloudId.isAcceptableOrUnknown(data['cloud_id']!, _cloudIdMeta),
);
}
if (data.containsKey('e_tag')) {
if (data.containsKey('created_at')) {
context.handle(
_eTagMeta,
eTag.isAcceptableOrUnknown(data['e_tag']!, _eTagMeta),
_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
);
}
if (data.containsKey('adjustment_time')) {
context.handle(
_adjustmentTimeMeta,
adjustmentTime.isAcceptableOrUnknown(
data['adjustment_time']!,
_adjustmentTimeMeta,
),
);
}
if (data.containsKey('latitude')) {
context.handle(
_latitudeMeta,
latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta),
);
}
if (data.containsKey('longitude')) {
context.handle(
_longitudeMeta,
longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta),
);
}
return context;
@@ -446,9 +567,21 @@ class $RemoteAssetCloudIdEntityTable extends i2.RemoteAssetCloudIdEntity
i0.DriftSqlType.string,
data['${effectivePrefix}cloud_id'],
),
eTag: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}e_tag'],
createdAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
),
adjustmentTime: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}adjustment_time'],
),
latitude: attachedDatabase.typeMapping.read(
i0.DriftSqlType.double,
data['${effectivePrefix}latitude'],
),
longitude: attachedDatabase.typeMapping.read(
i0.DriftSqlType.double,
data['${effectivePrefix}longitude'],
),
);
}
@@ -468,11 +601,17 @@ class RemoteAssetCloudIdEntityData extends i0.DataClass
implements i0.Insertable<i1.RemoteAssetCloudIdEntityData> {
final String assetId;
final String? cloudId;
final String? eTag;
final DateTime? createdAt;
final DateTime? adjustmentTime;
final double? latitude;
final double? longitude;
const RemoteAssetCloudIdEntityData({
required this.assetId,
this.cloudId,
this.eTag,
this.createdAt,
this.adjustmentTime,
this.latitude,
this.longitude,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
@@ -481,8 +620,17 @@ class RemoteAssetCloudIdEntityData extends i0.DataClass
if (!nullToAbsent || cloudId != null) {
map['cloud_id'] = i0.Variable<String>(cloudId);
}
if (!nullToAbsent || eTag != null) {
map['e_tag'] = i0.Variable<String>(eTag);
if (!nullToAbsent || createdAt != null) {
map['created_at'] = i0.Variable<DateTime>(createdAt);
}
if (!nullToAbsent || adjustmentTime != null) {
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime);
}
if (!nullToAbsent || latitude != null) {
map['latitude'] = i0.Variable<double>(latitude);
}
if (!nullToAbsent || longitude != null) {
map['longitude'] = i0.Variable<double>(longitude);
}
return map;
}
@@ -495,7 +643,10 @@ class RemoteAssetCloudIdEntityData extends i0.DataClass
return RemoteAssetCloudIdEntityData(
assetId: serializer.fromJson<String>(json['assetId']),
cloudId: serializer.fromJson<String?>(json['cloudId']),
eTag: serializer.fromJson<String?>(json['eTag']),
createdAt: serializer.fromJson<DateTime?>(json['createdAt']),
adjustmentTime: serializer.fromJson<DateTime?>(json['adjustmentTime']),
latitude: serializer.fromJson<double?>(json['latitude']),
longitude: serializer.fromJson<double?>(json['longitude']),
);
}
@override
@@ -504,18 +655,29 @@ class RemoteAssetCloudIdEntityData extends i0.DataClass
return <String, dynamic>{
'assetId': serializer.toJson<String>(assetId),
'cloudId': serializer.toJson<String?>(cloudId),
'eTag': serializer.toJson<String?>(eTag),
'createdAt': serializer.toJson<DateTime?>(createdAt),
'adjustmentTime': serializer.toJson<DateTime?>(adjustmentTime),
'latitude': serializer.toJson<double?>(latitude),
'longitude': serializer.toJson<double?>(longitude),
};
}
i1.RemoteAssetCloudIdEntityData copyWith({
String? assetId,
i0.Value<String?> cloudId = const i0.Value.absent(),
i0.Value<String?> eTag = const i0.Value.absent(),
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
i0.Value<double?> latitude = const i0.Value.absent(),
i0.Value<double?> longitude = const i0.Value.absent(),
}) => i1.RemoteAssetCloudIdEntityData(
assetId: assetId ?? this.assetId,
cloudId: cloudId.present ? cloudId.value : this.cloudId,
eTag: eTag.present ? eTag.value : this.eTag,
createdAt: createdAt.present ? createdAt.value : this.createdAt,
adjustmentTime: adjustmentTime.present
? adjustmentTime.value
: this.adjustmentTime,
latitude: latitude.present ? latitude.value : this.latitude,
longitude: longitude.present ? longitude.value : this.longitude,
);
RemoteAssetCloudIdEntityData copyWithCompanion(
i1.RemoteAssetCloudIdEntityCompanion data,
@@ -523,7 +685,12 @@ class RemoteAssetCloudIdEntityData extends i0.DataClass
return RemoteAssetCloudIdEntityData(
assetId: data.assetId.present ? data.assetId.value : this.assetId,
cloudId: data.cloudId.present ? data.cloudId.value : this.cloudId,
eTag: data.eTag.present ? data.eTag.value : this.eTag,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
adjustmentTime: data.adjustmentTime.present
? data.adjustmentTime.value
: this.adjustmentTime,
latitude: data.latitude.present ? data.latitude.value : this.latitude,
longitude: data.longitude.present ? data.longitude.value : this.longitude,
);
}
@@ -532,58 +699,92 @@ class RemoteAssetCloudIdEntityData extends i0.DataClass
return (StringBuffer('RemoteAssetCloudIdEntityData(')
..write('assetId: $assetId, ')
..write('cloudId: $cloudId, ')
..write('eTag: $eTag')
..write('createdAt: $createdAt, ')
..write('adjustmentTime: $adjustmentTime, ')
..write('latitude: $latitude, ')
..write('longitude: $longitude')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(assetId, cloudId, eTag);
int get hashCode => Object.hash(
assetId,
cloudId,
createdAt,
adjustmentTime,
latitude,
longitude,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.RemoteAssetCloudIdEntityData &&
other.assetId == this.assetId &&
other.cloudId == this.cloudId &&
other.eTag == this.eTag);
other.createdAt == this.createdAt &&
other.adjustmentTime == this.adjustmentTime &&
other.latitude == this.latitude &&
other.longitude == this.longitude);
}
class RemoteAssetCloudIdEntityCompanion
extends i0.UpdateCompanion<i1.RemoteAssetCloudIdEntityData> {
final i0.Value<String> assetId;
final i0.Value<String?> cloudId;
final i0.Value<String?> eTag;
final i0.Value<DateTime?> createdAt;
final i0.Value<DateTime?> adjustmentTime;
final i0.Value<double?> latitude;
final i0.Value<double?> longitude;
const RemoteAssetCloudIdEntityCompanion({
this.assetId = const i0.Value.absent(),
this.cloudId = const i0.Value.absent(),
this.eTag = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.adjustmentTime = const i0.Value.absent(),
this.latitude = const i0.Value.absent(),
this.longitude = const i0.Value.absent(),
});
RemoteAssetCloudIdEntityCompanion.insert({
required String assetId,
this.cloudId = const i0.Value.absent(),
this.eTag = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.adjustmentTime = const i0.Value.absent(),
this.latitude = const i0.Value.absent(),
this.longitude = const i0.Value.absent(),
}) : assetId = i0.Value(assetId);
static i0.Insertable<i1.RemoteAssetCloudIdEntityData> custom({
i0.Expression<String>? assetId,
i0.Expression<String>? cloudId,
i0.Expression<String>? eTag,
i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? adjustmentTime,
i0.Expression<double>? latitude,
i0.Expression<double>? longitude,
}) {
return i0.RawValuesInsertable({
if (assetId != null) 'asset_id': assetId,
if (cloudId != null) 'cloud_id': cloudId,
if (eTag != null) 'e_tag': eTag,
if (createdAt != null) 'created_at': createdAt,
if (adjustmentTime != null) 'adjustment_time': adjustmentTime,
if (latitude != null) 'latitude': latitude,
if (longitude != null) 'longitude': longitude,
});
}
i1.RemoteAssetCloudIdEntityCompanion copyWith({
i0.Value<String>? assetId,
i0.Value<String?>? cloudId,
i0.Value<String?>? eTag,
i0.Value<DateTime?>? createdAt,
i0.Value<DateTime?>? adjustmentTime,
i0.Value<double?>? latitude,
i0.Value<double?>? longitude,
}) {
return i1.RemoteAssetCloudIdEntityCompanion(
assetId: assetId ?? this.assetId,
cloudId: cloudId ?? this.cloudId,
eTag: eTag ?? this.eTag,
createdAt: createdAt ?? this.createdAt,
adjustmentTime: adjustmentTime ?? this.adjustmentTime,
latitude: latitude ?? this.latitude,
longitude: longitude ?? this.longitude,
);
}
@@ -596,8 +797,17 @@ class RemoteAssetCloudIdEntityCompanion
if (cloudId.present) {
map['cloud_id'] = i0.Variable<String>(cloudId.value);
}
if (eTag.present) {
map['e_tag'] = i0.Variable<String>(eTag.value);
if (createdAt.present) {
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
}
if (adjustmentTime.present) {
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime.value);
}
if (latitude.present) {
map['latitude'] = i0.Variable<double>(latitude.value);
}
if (longitude.present) {
map['longitude'] = i0.Variable<double>(longitude.value);
}
return map;
}
@@ -607,7 +817,10 @@ class RemoteAssetCloudIdEntityCompanion
return (StringBuffer('RemoteAssetCloudIdEntityCompanion(')
..write('assetId: $assetId, ')
..write('cloudId: $cloudId, ')
..write('eTag: $eTag')
..write('createdAt: $createdAt, ')
..write('adjustmentTime: $adjustmentTime, ')
..write('latitude: $latitude, ')
..write('longitude: $longitude')
..write(')'))
.toString();
}

View File

@@ -6243,7 +6243,14 @@ final class Schema15 extends i0.VersionedSchema {
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id)'],
columns: [_column_36, _column_98, _column_99],
columns: [
_column_36,
_column_98,
_column_99,
_column_96,
_column_46,
_column_47,
],
attachedDatabase: database,
),
alias: null,
@@ -6424,8 +6431,14 @@ class Shape26 extends i0.VersionedTable {
columnsByName['asset_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get cloudId =>
columnsByName['cloud_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get eTag =>
columnsByName['e_tag']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get adjustmentTime =>
columnsByName['adjustment_time']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<double> get latitude =>
columnsByName['latitude']! as i1.GeneratedColumn<double>;
i1.GeneratedColumn<double> get longitude =>
columnsByName['longitude']! as i1.GeneratedColumn<double>;
}
i1.GeneratedColumn<String> _column_98(String aliasedName) =>
@@ -6436,12 +6449,12 @@ i1.GeneratedColumn<String> _column_98(String aliasedName) =>
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways('UNIQUE'),
);
i1.GeneratedColumn<String> _column_99(String aliasedName) =>
i1.GeneratedColumn<String>(
'e_tag',
i1.GeneratedColumn<DateTime> _column_99(String aliasedName) =>
i1.GeneratedColumn<DateTime>(
'created_at',
aliasedName,
true,
type: i1.DriftSqlType.string,
type: i1.DriftSqlType.dateTime,
);
i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,

View File

@@ -5,7 +5,6 @@ import 'package:drift/drift.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/drift_extensions.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/entities/local_asset.entity.drift.dart';
@@ -136,20 +135,6 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
}
Future<Map<String, String>> getHashMappingFromCloudId() async {
final createdAt = _db.localAssetEntity.createdAt.strftime('%s');
final adjustmentTime = coalesce([_db.localAssetEntity.adjustmentTime.strftime('%s'), const Constant('0')]);
final latitude = coalesce([
_db.localAssetEntity.latitude,
const Constant(0.0),
]).truncateTo(2).cast(DriftSqlType.string);
final longitude = coalesce([
_db.localAssetEntity.longitude,
const Constant(0.0),
]).truncateTo(2).cast(DriftSqlType.string);
final delimiter = const Constant(kUploadETagDelimiter);
final eTag = createdAt + delimiter + adjustmentTime + delimiter + latitude + delimiter + longitude;
final query =
_db.localAssetEntity.selectOnly().join([
leftOuterJoin(
@@ -167,7 +152,10 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
..where(
_db.remoteAssetCloudIdEntity.cloudId.isNotNull() &
_db.localAssetEntity.checksum.isNull() &
_db.remoteAssetCloudIdEntity.eTag.equalsExp(eTag),
((_db.remoteAssetCloudIdEntity.adjustmentTime.isExp(_db.localAssetEntity.adjustmentTime)) &
(_db.remoteAssetCloudIdEntity.latitude.isExp(_db.localAssetEntity.latitude)) &
(_db.remoteAssetCloudIdEntity.longitude.isExp(_db.localAssetEntity.longitude)) &
(_db.remoteAssetCloudIdEntity.createdAt.isExp(_db.localAssetEntity.createdAt))),
);
final mapping = await query
.map(

View File

@@ -283,7 +283,12 @@ class SyncStreamRepository extends DriftDatabaseRepository {
final map = metadata.value as Map<String, Object?>;
final companion = RemoteAssetCloudIdEntityCompanion(
cloudId: Value(map['iCloudId']?.toString()),
eTag: Value(map['eTag']?.toString()),
createdAt: Value(map['createdAt'] != null ? DateTime.parse(map['createdAt'] as String) : null),
adjustmentTime: Value(
map['adjustmentTime'] != null ? DateTime.parse(map['adjustmentTime'] as String) : null,
),
latitude: Value(map['latitude'] != null ? (double.tryParse(map['latitude'] as String)) : null),
longitude: Value(map['longitude'] != null ? (double.tryParse(map['longitude'] as String)) : null),
);
batch.insert(
_db.remoteAssetCloudIdEntity,

View File

@@ -359,7 +359,9 @@ class UploadService {
isFavorite: asset.isFavorite,
requiresWiFi: requiresWiFi,
cloudId: asset.cloudId,
eTag: asset.eTag,
adjustmentTime: asset.adjustmentTime?.toIso8601String(),
latitude: asset.latitude?.toString(),
longitude: asset.longitude?.toString(),
);
}
@@ -392,7 +394,9 @@ class UploadService {
isFavorite: asset.isFavorite,
requiresWiFi: requiresWiFi,
cloudId: asset.cloudId,
eTag: asset.eTag,
adjustmentTime: asset.adjustmentTime?.toIso8601String(),
latitude: asset.latitude?.toString(),
longitude: asset.longitude?.toString(),
);
}
@@ -421,7 +425,9 @@ class UploadService {
bool? isFavorite,
bool requiresWiFi = true,
String? cloudId,
String? eTag,
String? adjustmentTime,
String? latitude,
String? longitude,
}) async {
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
final url = Uri.parse('$serverEndpoint/assets').toString();
@@ -438,14 +444,17 @@ class UploadService {
'duration': '0',
if (fields != null) ...fields,
// Include cloudId and eTag in metadata if available and server version supports it
if (CurrentPlatform.isIOS &&
cloudId != null &&
eTag != null &&
_serverInfo.serverVersion.isAtLeast(major: 2, minor: 2))
if (CurrentPlatform.isIOS && cloudId != null && _serverInfo.serverVersion.isAtLeast(major: 2, minor: 2))
'metadata': jsonEncode([
RemoteAssetMetadataItem(
key: RemoteAssetMetadataKey.mobileApp,
value: RemoteAssetMobileAppMetadata(cloudId: cloudId, eTag: eTag),
value: RemoteAssetMobileAppMetadata(
cloudId: cloudId,
createdAt: createdAt.toIso8601String(),
adjustmentTime: adjustmentTime,
latitude: latitude,
longitude: longitude,
),
),
]),
};

View File

@@ -5393,15 +5393,44 @@ class RemoteAssetCloudIdEntity extends Table
requiredDuringInsert: false,
defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'),
);
late final GeneratedColumn<String> eTag = GeneratedColumn<String>(
'e_tag',
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at',
aliasedName,
true,
type: DriftSqlType.string,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
);
late final GeneratedColumn<DateTime> adjustmentTime =
GeneratedColumn<DateTime>(
'adjustment_time',
aliasedName,
true,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
);
late final GeneratedColumn<double> latitude = GeneratedColumn<double>(
'latitude',
aliasedName,
true,
type: DriftSqlType.double,
requiredDuringInsert: false,
);
late final GeneratedColumn<double> longitude = GeneratedColumn<double>(
'longitude',
aliasedName,
true,
type: DriftSqlType.double,
requiredDuringInsert: false,
);
@override
List<GeneratedColumn> get $columns => [assetId, cloudId, eTag];
List<GeneratedColumn> get $columns => [
assetId,
cloudId,
createdAt,
adjustmentTime,
latitude,
longitude,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
@@ -5424,9 +5453,21 @@ class RemoteAssetCloudIdEntity extends Table
DriftSqlType.string,
data['${effectivePrefix}cloud_id'],
),
eTag: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}e_tag'],
createdAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
),
adjustmentTime: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime,
data['${effectivePrefix}adjustment_time'],
),
latitude: attachedDatabase.typeMapping.read(
DriftSqlType.double,
data['${effectivePrefix}latitude'],
),
longitude: attachedDatabase.typeMapping.read(
DriftSqlType.double,
data['${effectivePrefix}longitude'],
),
);
}
@@ -5446,11 +5487,17 @@ class RemoteAssetCloudIdEntityData extends DataClass
implements Insertable<RemoteAssetCloudIdEntityData> {
final String assetId;
final String? cloudId;
final String? eTag;
final DateTime? createdAt;
final DateTime? adjustmentTime;
final double? latitude;
final double? longitude;
const RemoteAssetCloudIdEntityData({
required this.assetId,
this.cloudId,
this.eTag,
this.createdAt,
this.adjustmentTime,
this.latitude,
this.longitude,
});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
@@ -5459,8 +5506,17 @@ class RemoteAssetCloudIdEntityData extends DataClass
if (!nullToAbsent || cloudId != null) {
map['cloud_id'] = Variable<String>(cloudId);
}
if (!nullToAbsent || eTag != null) {
map['e_tag'] = Variable<String>(eTag);
if (!nullToAbsent || createdAt != null) {
map['created_at'] = Variable<DateTime>(createdAt);
}
if (!nullToAbsent || adjustmentTime != null) {
map['adjustment_time'] = Variable<DateTime>(adjustmentTime);
}
if (!nullToAbsent || latitude != null) {
map['latitude'] = Variable<double>(latitude);
}
if (!nullToAbsent || longitude != null) {
map['longitude'] = Variable<double>(longitude);
}
return map;
}
@@ -5473,7 +5529,10 @@ class RemoteAssetCloudIdEntityData extends DataClass
return RemoteAssetCloudIdEntityData(
assetId: serializer.fromJson<String>(json['assetId']),
cloudId: serializer.fromJson<String?>(json['cloudId']),
eTag: serializer.fromJson<String?>(json['eTag']),
createdAt: serializer.fromJson<DateTime?>(json['createdAt']),
adjustmentTime: serializer.fromJson<DateTime?>(json['adjustmentTime']),
latitude: serializer.fromJson<double?>(json['latitude']),
longitude: serializer.fromJson<double?>(json['longitude']),
);
}
@override
@@ -5482,18 +5541,29 @@ class RemoteAssetCloudIdEntityData extends DataClass
return <String, dynamic>{
'assetId': serializer.toJson<String>(assetId),
'cloudId': serializer.toJson<String?>(cloudId),
'eTag': serializer.toJson<String?>(eTag),
'createdAt': serializer.toJson<DateTime?>(createdAt),
'adjustmentTime': serializer.toJson<DateTime?>(adjustmentTime),
'latitude': serializer.toJson<double?>(latitude),
'longitude': serializer.toJson<double?>(longitude),
};
}
RemoteAssetCloudIdEntityData copyWith({
String? assetId,
Value<String?> cloudId = const Value.absent(),
Value<String?> eTag = const Value.absent(),
Value<DateTime?> createdAt = const Value.absent(),
Value<DateTime?> adjustmentTime = const Value.absent(),
Value<double?> latitude = const Value.absent(),
Value<double?> longitude = const Value.absent(),
}) => RemoteAssetCloudIdEntityData(
assetId: assetId ?? this.assetId,
cloudId: cloudId.present ? cloudId.value : this.cloudId,
eTag: eTag.present ? eTag.value : this.eTag,
createdAt: createdAt.present ? createdAt.value : this.createdAt,
adjustmentTime: adjustmentTime.present
? adjustmentTime.value
: this.adjustmentTime,
latitude: latitude.present ? latitude.value : this.latitude,
longitude: longitude.present ? longitude.value : this.longitude,
);
RemoteAssetCloudIdEntityData copyWithCompanion(
RemoteAssetCloudIdEntityCompanion data,
@@ -5501,7 +5571,12 @@ class RemoteAssetCloudIdEntityData extends DataClass
return RemoteAssetCloudIdEntityData(
assetId: data.assetId.present ? data.assetId.value : this.assetId,
cloudId: data.cloudId.present ? data.cloudId.value : this.cloudId,
eTag: data.eTag.present ? data.eTag.value : this.eTag,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
adjustmentTime: data.adjustmentTime.present
? data.adjustmentTime.value
: this.adjustmentTime,
latitude: data.latitude.present ? data.latitude.value : this.latitude,
longitude: data.longitude.present ? data.longitude.value : this.longitude,
);
}
@@ -5510,58 +5585,92 @@ class RemoteAssetCloudIdEntityData extends DataClass
return (StringBuffer('RemoteAssetCloudIdEntityData(')
..write('assetId: $assetId, ')
..write('cloudId: $cloudId, ')
..write('eTag: $eTag')
..write('createdAt: $createdAt, ')
..write('adjustmentTime: $adjustmentTime, ')
..write('latitude: $latitude, ')
..write('longitude: $longitude')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(assetId, cloudId, eTag);
int get hashCode => Object.hash(
assetId,
cloudId,
createdAt,
adjustmentTime,
latitude,
longitude,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is RemoteAssetCloudIdEntityData &&
other.assetId == this.assetId &&
other.cloudId == this.cloudId &&
other.eTag == this.eTag);
other.createdAt == this.createdAt &&
other.adjustmentTime == this.adjustmentTime &&
other.latitude == this.latitude &&
other.longitude == this.longitude);
}
class RemoteAssetCloudIdEntityCompanion
extends UpdateCompanion<RemoteAssetCloudIdEntityData> {
final Value<String> assetId;
final Value<String?> cloudId;
final Value<String?> eTag;
final Value<DateTime?> createdAt;
final Value<DateTime?> adjustmentTime;
final Value<double?> latitude;
final Value<double?> longitude;
const RemoteAssetCloudIdEntityCompanion({
this.assetId = const Value.absent(),
this.cloudId = const Value.absent(),
this.eTag = const Value.absent(),
this.createdAt = const Value.absent(),
this.adjustmentTime = const Value.absent(),
this.latitude = const Value.absent(),
this.longitude = const Value.absent(),
});
RemoteAssetCloudIdEntityCompanion.insert({
required String assetId,
this.cloudId = const Value.absent(),
this.eTag = const Value.absent(),
this.createdAt = const Value.absent(),
this.adjustmentTime = const Value.absent(),
this.latitude = const Value.absent(),
this.longitude = const Value.absent(),
}) : assetId = Value(assetId);
static Insertable<RemoteAssetCloudIdEntityData> custom({
Expression<String>? assetId,
Expression<String>? cloudId,
Expression<String>? eTag,
Expression<DateTime>? createdAt,
Expression<DateTime>? adjustmentTime,
Expression<double>? latitude,
Expression<double>? longitude,
}) {
return RawValuesInsertable({
if (assetId != null) 'asset_id': assetId,
if (cloudId != null) 'cloud_id': cloudId,
if (eTag != null) 'e_tag': eTag,
if (createdAt != null) 'created_at': createdAt,
if (adjustmentTime != null) 'adjustment_time': adjustmentTime,
if (latitude != null) 'latitude': latitude,
if (longitude != null) 'longitude': longitude,
});
}
RemoteAssetCloudIdEntityCompanion copyWith({
Value<String>? assetId,
Value<String?>? cloudId,
Value<String?>? eTag,
Value<DateTime?>? createdAt,
Value<DateTime?>? adjustmentTime,
Value<double?>? latitude,
Value<double?>? longitude,
}) {
return RemoteAssetCloudIdEntityCompanion(
assetId: assetId ?? this.assetId,
cloudId: cloudId ?? this.cloudId,
eTag: eTag ?? this.eTag,
createdAt: createdAt ?? this.createdAt,
adjustmentTime: adjustmentTime ?? this.adjustmentTime,
latitude: latitude ?? this.latitude,
longitude: longitude ?? this.longitude,
);
}
@@ -5574,8 +5683,17 @@ class RemoteAssetCloudIdEntityCompanion
if (cloudId.present) {
map['cloud_id'] = Variable<String>(cloudId.value);
}
if (eTag.present) {
map['e_tag'] = Variable<String>(eTag.value);
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
if (adjustmentTime.present) {
map['adjustment_time'] = Variable<DateTime>(adjustmentTime.value);
}
if (latitude.present) {
map['latitude'] = Variable<double>(latitude.value);
}
if (longitude.present) {
map['longitude'] = Variable<double>(longitude.value);
}
return map;
}
@@ -5585,7 +5703,10 @@ class RemoteAssetCloudIdEntityCompanion
return (StringBuffer('RemoteAssetCloudIdEntityCompanion(')
..write('assetId: $assetId, ')
..write('cloudId: $cloudId, ')
..write('eTag: $eTag')
..write('createdAt: $createdAt, ')
..write('adjustmentTime: $adjustmentTime, ')
..write('latitude: $latitude, ')
..write('longitude: $longitude')
..write(')'))
.toString();
}