diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index 9c618d9ed0..84158734e7 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -89,7 +89,8 @@ data class PlatformAsset ( val height: Long? = null, val durationInSeconds: Long, val orientation: Long, - val isFavorite: Boolean + val isFavorite: Boolean, + val size: Long? = null ) { companion object { @@ -104,7 +105,8 @@ data class PlatformAsset ( val durationInSeconds = pigeonVar_list[7] as Long val orientation = pigeonVar_list[8] as Long val isFavorite = pigeonVar_list[9] as Boolean - return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite) + val size = pigeonVar_list[10] as Long? + return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite, size) } } fun toList(): List { @@ -119,6 +121,7 @@ data class PlatformAsset ( durationInSeconds, orientation, isFavorite, + size, ) } override fun equals(other: Any?): Boolean { @@ -209,6 +212,40 @@ data class SyncDelta ( override fun hashCode(): Int = toList().hashCode() } + +/** Generated class from Pigeon that represents data sent in messages. */ +data class TrashedAssetParams ( + val id: String, + val type: Long, + val albumId: String? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): TrashedAssetParams { + val id = pigeonVar_list[0] as String + val type = pigeonVar_list[1] as Long + val albumId = pigeonVar_list[2] as String? + return TrashedAssetParams(id, type, albumId) + } + } + fun toList(): List { + return listOf( + id, + type, + albumId, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is TrashedAssetParams) { + return false + } + if (this === other) { + return true + } + return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} private open class MessagesPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { @@ -227,6 +264,11 @@ private open class MessagesPigeonCodec : StandardMessageCodec() { SyncDelta.fromList(it) } } + 132.toByte() -> { + return (readValue(buffer) as? List)?.let { + TrashedAssetParams.fromList(it) + } + } else -> super.readValueOfType(type, buffer) } } @@ -244,6 +286,10 @@ private open class MessagesPigeonCodec : StandardMessageCodec() { stream.write(131) writeValue(stream, value.toList()) } + is TrashedAssetParams -> { + stream.write(132) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -260,6 +306,8 @@ interface NativeSyncApi { fun getAssetsCountSince(albumId: String, timestamp: Long): Long fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List fun hashPaths(paths: List): List + fun getTrashedAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List + fun hashTrashedAssets(trashedAssets: List): List companion object { /** The codec used by NativeSyncApi. */ @@ -418,6 +466,41 @@ interface NativeSyncApi { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssetsForAlbum$separatedMessageChannelSuffix", codec, taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val albumIdArg = args[0] as String + val updatedTimeCondArg = args[1] as Long? + val wrapped: List = try { + listOf(api.getTrashedAssetsForAlbum(albumIdArg, updatedTimeCondArg)) + } catch (exception: Throwable) { + MessagesPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashTrashedAssets$separatedMessageChannelSuffix", codec, taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val trashedAssetsArg = args[0] as List + val wrapped: List = try { + listOf(api.hashTrashedAssets(trashedAssetsArg)) + } catch (exception: Throwable) { + MessagesPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } } } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl26.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl26.kt index 5deacc30db..a39344295d 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl26.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl26.kt @@ -21,4 +21,15 @@ class NativeSyncApiImpl26(context: Context) : NativeSyncApiImplBase(context), Na override fun getMediaChanges(): SyncDelta { throw IllegalStateException("Method not supported on this Android version.") } + + override fun getTrashedAssetsForAlbum( + albumId: String, + updatedTimeCond: Long? + ): List { + throw IllegalStateException("Method not supported on this Android version.") + } + + override fun hashTrashedAssets(trashedAssets: List): List { + throw IllegalStateException("Method not supported on this Android version.") + } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl30.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl30.kt index 052032e143..58db33896b 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl30.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl30.kt @@ -1,11 +1,18 @@ package app.alextran.immich.sync +import android.content.ContentResolver +import android.content.ContentUris import android.content.Context +import android.net.Uri import android.os.Build +import android.os.Bundle import android.provider.MediaStore import androidx.annotation.RequiresApi import androidx.annotation.RequiresExtension import kotlinx.serialization.json.Json +import java.io.BufferedInputStream +import java.security.DigestInputStream +import java.security.MessageDigest @RequiresApi(Build.VERSION_CODES.Q) @RequiresExtension(extension = Build.VERSION_CODES.R, version = 1) @@ -33,6 +40,56 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na } } + @RequiresExtension(extension = Build.VERSION_CODES.R, version = 1) + @RequiresApi(Build.VERSION_CODES.R) + override fun getTrashedAssetsForAlbum( + albumId: String, + updatedTimeCond: Long? + ): List { + val trashed = mutableListOf() + val volumes = MediaStore.getExternalVolumeNames(ctx) + + var selection = "$BUCKET_SELECTION AND $MEDIA_SELECTION" + val selectionArgs = mutableListOf(albumId, *MEDIA_SELECTION_ARGS) + + if (updatedTimeCond != null) { + selection += " AND (${MediaStore.Files.FileColumns.DATE_MODIFIED} > ? OR ${MediaStore.Files.FileColumns.DATE_ADDED} > ?)" + selectionArgs.addAll(listOf(updatedTimeCond.toString(), updatedTimeCond.toString())) + } + + for (volume in volumes) { + val uri = MediaStore.Files.getContentUri(volume) + val queryArgs = Bundle().apply { + putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection) + putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs.toTypedArray()) + putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_ONLY) + } + + ctx.contentResolver.query(uri, ASSET_PROJECTION, queryArgs, null).use { cursor -> + getAssets(cursor).forEach { res -> + if (res is AssetResult.ValidAsset) trashed += res.asset + } + } + } + return trashed + } + + override fun hashTrashedAssets(trashedAssets: List): List { + val result = ArrayList(trashedAssets.size) + for (item in trashedAssets) { + val digest = try { + val id = item.id.toLong() + val mediaType = item.type.toInt() + val uri = contentUriForType(id, mediaType) + sha1OfUri(ctx, uri) + } catch (_: Throwable) { + null + } + result.add(digest) + } + return result + } + override fun shouldFullSync(): Boolean = MediaStore.getVersion(ctx) != prefs.getString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, null) @@ -86,4 +143,31 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na // Unmounted volumes are handled in dart when the album is removed return SyncDelta(hasChanges, changed, deleted, assetAlbums) } + + private fun contentUriForType(id: Long, mediaType: Int): Uri { + val vol = MediaStore.VOLUME_EXTERNAL + val base = when (mediaType) { + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE -> MediaStore.Images.Media.getContentUri(vol) + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO -> MediaStore.Video.Media.getContentUri(vol) + MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO -> MediaStore.Audio.Media.getContentUri(vol) + else -> MediaStore.Files.getContentUri(vol) + } + return ContentUris.withAppendedId(base, id) + } + + private fun sha1OfUri(ctx: Context, uri: Uri): ByteArray? { + return try { + val md = MessageDigest.getInstance("SHA-1") + ctx.contentResolver.openInputStream(uri)?.use { input -> + DigestInputStream(BufferedInputStream(input), md).use { dis -> + val buf = ByteArray(DEFAULT_BUFFER_SIZE) + while (dis.read(buf) != -1) { /* pump */ + } + } + } ?: return null + md.digest() + } catch (_: Exception) { + null + } + } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index b2ceb8a9f2..dbe53e3642 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -46,6 +46,7 @@ open class NativeSyncApiImplBase(context: Context) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { add(MediaStore.MediaColumns.IS_FAVORITE) } + add(MediaStore.MediaColumns.SIZE) }.toTypedArray() const val HASH_BUFFER_SIZE = 2 * 1024 * 1024 @@ -82,6 +83,7 @@ open class NativeSyncApiImplBase(context: Context) { val orientationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.ORIENTATION) val favoriteColumn = c.getColumnIndex(MediaStore.MediaColumns.IS_FAVORITE) + val sizeColumn = c.getColumnIndex(MediaStore.MediaColumns.SIZE) while (c.moveToNext()) { val id = c.getLong(idColumn).toString() @@ -111,7 +113,7 @@ open class NativeSyncApiImplBase(context: Context) { val bucketId = c.getString(bucketIdColumn) val orientation = c.getInt(orientationColumn) val isFavorite = if (favoriteColumn == -1) false else c.getInt(favoriteColumn) != 0 - + val size = c.getLong(sizeColumn) val asset = PlatformAsset( id, name, @@ -123,6 +125,7 @@ open class NativeSyncApiImplBase(context: Context) { duration, orientation.toLong(), isFavorite, + size ) yield(AssetResult.ValidAsset(asset, bucketId)) } diff --git a/mobile/drift_schemas/main/drift_schema_v11.json b/mobile/drift_schemas/main/drift_schema_v11.json index f5091c70c4..3e858de032 100644 --- a/mobile/drift_schemas/main/drift_schema_v11.json +++ b/mobile/drift_schemas/main/drift_schema_v11.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[4],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"linked_remote_album_id","getter_name":"linkedRemoteAlbumId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":6,"references":[3,5],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":7,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":11,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":12,"references":[],"type":"table","data":{"name":"auth_user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"pin_code","getter_name":"pinCode","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":14,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":15,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":16,"references":[1,4],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":17,"references":[4,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":18,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":19,"references":[1,18],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":20,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[1,20],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":22,"references":[],"type":"table","data":{"name":"store_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"string_value","getter_name":"stringValue","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"int_value","getter_name":"intValue","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":23,"references":[1],"type":"table","data":{"name":"local_trashed_asset_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"remote_id","getter_name":"remoteId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":24,"references":[15],"type":"index","data":{"on":15,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}},{"id":25,"references":[23],"type":"index","data":{"on":23,"name":"idx_local_trashed_asset_remote_id","sql":"CREATE INDEX IF NOT EXISTS idx_local_trashed_asset_remote_id ON local_trashed_asset_entity (remote_id)","unique":false,"columns":[]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[4],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"linked_remote_album_id","getter_name":"linkedRemoteAlbumId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":6,"references":[3,5],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":7,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":11,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":12,"references":[],"type":"table","data":{"name":"auth_user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"pin_code","getter_name":"pinCode","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":14,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":15,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":16,"references":[1,4],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":17,"references":[4,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":18,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":19,"references":[1,18],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":20,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[1,20],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":22,"references":[],"type":"table","data":{"name":"store_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"string_value","getter_name":"stringValue","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"int_value","getter_name":"intValue","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":23,"references":[],"type":"table","data":{"name":"trashed_local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"size","getter_name":"size","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":24,"references":[15],"type":"index","data":{"on":15,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}},{"id":25,"references":[23],"type":"index","data":{"on":23,"name":"idx_trashed_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index 19f4384672..a69ea8b21c 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -140,6 +140,7 @@ struct PlatformAsset: Hashable { var durationInSeconds: Int64 var orientation: Int64 var isFavorite: Bool + var size: Int64? = nil // swift-format-ignore: AlwaysUseLowerCamelCase @@ -154,6 +155,7 @@ struct PlatformAsset: Hashable { let durationInSeconds = pigeonVar_list[7] as! Int64 let orientation = pigeonVar_list[8] as! Int64 let isFavorite = pigeonVar_list[9] as! Bool + let size: Int64? = nilOrValue(pigeonVar_list[10]) return PlatformAsset( id: id, @@ -165,7 +167,8 @@ struct PlatformAsset: Hashable { height: height, durationInSeconds: durationInSeconds, orientation: orientation, - isFavorite: isFavorite + isFavorite: isFavorite, + size: size ) } func toList() -> [Any?] { @@ -180,6 +183,7 @@ struct PlatformAsset: Hashable { durationInSeconds, orientation, isFavorite, + size, ] } static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool { @@ -267,6 +271,39 @@ struct SyncDelta: Hashable { } } +/// Generated class from Pigeon that represents data sent in messages. +struct TrashedAssetParams: Hashable { + var id: String + var type: Int64 + var albumId: String? = nil + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> TrashedAssetParams? { + let id = pigeonVar_list[0] as! String + let type = pigeonVar_list[1] as! Int64 + let albumId: String? = nilOrValue(pigeonVar_list[2]) + + return TrashedAssetParams( + id: id, + type: type, + albumId: albumId + ) + } + func toList() -> [Any?] { + return [ + id, + type, + albumId, + ] + } + static func == (lhs: TrashedAssetParams, rhs: TrashedAssetParams) -> Bool { + return deepEqualsMessages(lhs.toList(), rhs.toList()) } + func hash(into hasher: inout Hasher) { + deepHashMessages(value: toList(), hasher: &hasher) + } +} + private class MessagesPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { @@ -276,6 +313,8 @@ private class MessagesPigeonCodecReader: FlutterStandardReader { return PlatformAlbum.fromList(self.readValue() as! [Any?]) case 131: return SyncDelta.fromList(self.readValue() as! [Any?]) + case 132: + return TrashedAssetParams.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -293,6 +332,9 @@ private class MessagesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? SyncDelta { super.writeByte(131) super.writeValue(value.toList()) + } else if let value = value as? TrashedAssetParams { + super.writeByte(132) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -324,6 +366,8 @@ protocol NativeSyncApi { func getAssetsCountSince(albumId: String, timestamp: Int64) throws -> Int64 func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset] func hashPaths(paths: [String]) throws -> [FlutterStandardTypedData?] + func getTrashedAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset] + func hashTrashedAssets(trashedAssets: [TrashedAssetParams]) throws -> [FlutterStandardTypedData?] } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -476,5 +520,40 @@ class NativeSyncApiSetup { } else { hashPathsChannel.setMessageHandler(nil) } + let getTrashedAssetsForAlbumChannel = taskQueue == nil + ? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + : FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue) + if let api = api { + getTrashedAssetsForAlbumChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let albumIdArg = args[0] as! String + let updatedTimeCondArg: Int64? = nilOrValue(args[1]) + do { + let result = try api.getTrashedAssetsForAlbum(albumId: albumIdArg, updatedTimeCond: updatedTimeCondArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getTrashedAssetsForAlbumChannel.setMessageHandler(nil) + } + let hashTrashedAssetsChannel = taskQueue == nil + ? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashTrashedAssets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + : FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashTrashedAssets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue) + if let api = api { + hashTrashedAssetsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let trashedAssetsArg = args[0] as! [TrashedAssetParams] + do { + let result = try api.hashTrashedAssets(trashedAssets: trashedAssetsArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + hashTrashedAssetsChannel.setMessageHandler(nil) + } } } diff --git a/mobile/lib/domain/models/asset/trashed_asset.model.dart b/mobile/lib/domain/models/asset/trashed_asset.model.dart new file mode 100644 index 0000000000..782d8326dc --- /dev/null +++ b/mobile/lib/domain/models/asset/trashed_asset.model.dart @@ -0,0 +1,76 @@ +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; + +class TrashedAsset { + final String id; + final String name; + final String albumId; + final String? checksum; + final AssetType type; + final DateTime createdAt; + final DateTime updatedAt; + final int? size; + + const TrashedAsset({ + required this.id, + required this.name, + required this.checksum, + required this.albumId, + required this.type, + required this.createdAt, + required this.updatedAt, + this.size, + }); + + TrashedAsset copyWith({ + String? id, + String? name, + String? albumId, + String? checksum, + AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + int? size, + }) { + return TrashedAsset( + id: id ?? this.id, + name: name ?? this.name, + albumId: albumId ?? this.albumId, + checksum: checksum ?? this.checksum, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + size: size ?? this.size, + ); + } + + @override + String toString() { + return 'TrashedAsset(' + 'id: $id, ' + 'name: $name, ' + 'albumId: $albumId, ' + 'checksum: $checksum, ' + 'type: $type, ' + 'createdAt: $createdAt, ' + 'updatedAt: $updatedAt, ' + 'size: ${size ?? ""}' + ')'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is TrashedAsset && + runtimeType == other.runtimeType && + id == other.id && + name == other.name && + albumId == other.albumId && + checksum == other.checksum && + type == other.type && + createdAt == other.createdAt && + updatedAt == other.updatedAt && + size == other.size; + + @override + int get hashCode => Object.hash(id, name, albumId, checksum, type, createdAt, updatedAt, size); +} diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index 8044b298d3..a144f8bed9 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -3,6 +3,8 @@ import 'dart:convert'; 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/domain/models/asset/trashed_asset.model.dart'; +import 'package:immich_mobile/domain/services/trash_sync.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; @@ -16,6 +18,7 @@ class HashService { final DriftLocalAssetRepository _localAssetRepository; final StorageRepository _storageRepository; final NativeSyncApi _nativeSyncApi; + final TrashSyncService _trashSyncService; final bool Function()? _cancelChecker; final _log = Logger('HashService'); @@ -24,6 +27,7 @@ class HashService { required DriftLocalAssetRepository localAssetRepository, required StorageRepository storageRepository, required NativeSyncApi nativeSyncApi, + required TrashSyncService trashSyncService, bool Function()? cancelChecker, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, @@ -31,7 +35,8 @@ class HashService { _localAssetRepository = localAssetRepository, _storageRepository = storageRepository, _cancelChecker = cancelChecker, - _nativeSyncApi = nativeSyncApi; + _nativeSyncApi = nativeSyncApi, + _trashSyncService = trashSyncService; bool get isCancelled => _cancelChecker?.call() ?? false; @@ -55,6 +60,20 @@ class HashService { } } + if (_trashSyncService.isAutoSyncMode) { + final backupAlbums = await _localAlbumRepository.getBackupAlbums(); + for (final album in backupAlbums) { + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing albums."); + break; + } + final trashedToHash = await _trashSyncService.getAssetsToHash(album.id); + if (trashedToHash.isNotEmpty) { + await _hashTrashedAssets(album, trashedToHash); + } + } + } + stopwatch.stop(); _log.info("Hashing took - ${stopwatch.elapsedMilliseconds}ms"); } @@ -130,6 +149,130 @@ class HashService { await _localAssetRepository.updateHashes(hashed); await _storageRepository.clearCache(); } + + Future _hashTrashedAssets(LocalAlbum album, Iterable assetsToHash) async { + int bytesProcessed = 0; + final toHash = []; + + for (final asset in assetsToHash) { + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing assets."); + return; + } + + if (asset.size == null) { + _log.warning( + "Cannot get size for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt} from album: ${album.name}", + ); + continue; + } + + bytesProcessed += asset.size!; + toHash.add(asset); + + if (toHash.length >= batchFileLimit || bytesProcessed >= batchSizeLimit) { + await _processTrashedBatch(album, toHash); + toHash.clear(); + bytesProcessed = 0; + } + } + + await _processTrashedBatch(album, toHash); + } + + Future _processTrashedBatch(LocalAlbum album, List toHash) async { + if (toHash.isEmpty) { + return; + } + + _log.fine("Hashing ${toHash.length} trashed files"); + + final params = toHash.map((e) => TrashedAssetParams(id: e.id, type: e.type.index, albumId: album.id)).toList(); + final hashes = await _nativeSyncApi.hashTrashedAssets(params); + + assert( + hashes.length == toHash.length, + "Trashed Assets, Hashes length does not match toHash length: ${hashes.length} != ${toHash.length}", + ); + final hashed = []; + + for (int i = 0; i < hashes.length; i++) { + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing batch."); + return; + } + + final hash = hashes[i]; + final asset = toHash[i]; + if (hash?.length == 20) { + hashed.add(asset.copyWith(checksum: base64.encode(hash!))); + } else { + _log.warning( + "Failed to hash trashed file for ${asset.id}: ${asset.name} created at ${asset.createdAt} from album: ${album.name}", + ); + } + } + + _log.fine("Hashed ${hashed.length}/${toHash.length} trashed assets"); + await _trashSyncService.updateChecksums(hashed); + } + + // Future _hashTrashedAssets(LocalAlbum album, Iterable assetsToHash) async { + // final trashedAssets = assetsToHash + // .map((e) => TrashedAssetParams(id: e.id, type: e.type.index, albumId: album.id)) + // .toList(); + // + // final byId = {for (final a in assetsToHash) a.id: a}; + // + // final hashed = []; + // const chunkSize = 10; + // + // for (int i = 0; i < trashedAssets.length; i += chunkSize) { + // if (isCancelled) { + // _log.warning("Hashing cancelled. Stopped processing assets."); + // return; + // } + // final end = (i + chunkSize <= trashedAssets.length) ? i + chunkSize : trashedAssets.length; + // final batch = trashedAssets.sublist(i, end); + // + // List hashes; + // try { + // hashes = await _nativeSyncApi.hashTrashedAssets(batch); + // } catch (e, s) { + // _log.severe("hashTrashedAssets failed for batch [$i..${end - 1}]: $e", e, s); + // continue; + // } + // + // if (hashes.length != batch.length) { + // _log.warning( + // "hashTrashedAssets returned ${hashes.length} hashes for ${batch.length} assets (batch [$i..${end - 1}]).", + // ); + // } + // + // final limit = hashes.length < batch.length ? hashes.length : batch.length; + // for (int j = 0; j < limit; j++) { + // if (isCancelled) { + // _log.warning("Hashing cancelled. Stopped processing assets."); + // return; + // } + // final asset = batch[j]; + // final hash = hashes[j]; + // + // if (hash != null && hash.length == 20) { + // final localAsset = byId[asset.id]; + // if (localAsset != null) { + // hashed.add(localAsset.copyWith(checksum: base64.encode(hash))); + // } + // } else { + // _log.warning("Failed to hash file for ${asset.id} from album: ${album.name}"); + // } + // } + // } + // + // _log.warning("updateHashes for album: ${album.name}, assets: ${hashed.map((e) => '${e.name}-${e.checksum}')}"); + // + // await _trashSyncService.updateHashes(hashed); + // } } class _AssetToPath { diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index 5ab10bdf09..ffcd61aed8 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -4,6 +4,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/widgets.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/domain/services/trash_sync.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; import 'package:immich_mobile/utils/datetime_helpers.dart'; @@ -14,14 +15,17 @@ import 'package:platform/platform.dart'; class LocalSyncService { final DriftLocalAlbumRepository _localAlbumRepository; final NativeSyncApi _nativeSyncApi; + final TrashSyncService _trashSyncService; final Platform _platform; final Logger _log = Logger("DeviceSyncService"); LocalSyncService({ required DriftLocalAlbumRepository localAlbumRepository, + required TrashSyncService trashSyncService, required NativeSyncApi nativeSyncApi, Platform? platform, }) : _localAlbumRepository = localAlbumRepository, + _trashSyncService = trashSyncService, _nativeSyncApi = nativeSyncApi, _platform = platform ?? const LocalPlatform(); @@ -74,7 +78,10 @@ class LocalSyncService { await updateAlbum(dbAlbum, album); } } - + if (_trashSyncService.isAutoSyncMode) { + // On Android we need to sync trashed assets + await _trashSyncService.updateLocalTrashFromDevice(); + } await _nativeSyncApi.checkpointSync(); } catch (e, s) { _log.severe("Error performing device sync", e, s); @@ -99,6 +106,9 @@ class LocalSyncService { onlyFirst: removeAlbum, onlySecond: addAlbum, ); + if (_trashSyncService.isAutoSyncMode) { + await _trashSyncService.updateLocalTrashFromDevice(); + } await _nativeSyncApi.checkpointSync(); stopwatch.stop(); diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 50a42cb6b4..3109b4b493 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -86,9 +86,11 @@ class SyncStreamService { return _syncStreamRepository.deletePartnerV1(data.cast()); case SyncEntityType.assetV1: final remoteSyncAssets = data.cast(); - await _trashSyncService.handleRemoteChanges( - remoteSyncAssets.map((e) => (remoteId: e.id, checksum: e.checksum, deletedAt: e.deletedAt)), - ); + if (_trashSyncService.isAutoSyncMode) { + await _trashSyncService.handleRemoteChanges( + remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.checksum), + ); + } return _syncStreamRepository.updateAssetsV1(remoteSyncAssets); case SyncEntityType.assetDeleteV1: return _syncStreamRepository.deleteAssetsV1(data.cast()); diff --git a/mobile/lib/domain/services/trash_sync.service.dart b/mobile/lib/domain/services/trash_sync.service.dart index 43daaba80f..fc0c99a15b 100644 --- a/mobile/lib/domain/services/trash_sync.service.dart +++ b/mobile/lib/domain/services/trash_sync.service.dart @@ -1,10 +1,13 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/local_trashed_asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/trashed_asset.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart'; +import 'package:immich_mobile/platform/native_sync_api.g.dart'; import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/datetime_helpers.dart'; import 'package:logging/logging.dart'; import 'package:platform/platform.dart'; @@ -12,8 +15,10 @@ typedef TrashSyncItem = ({String remoteId, String checksum, DateTime? deletedAt} class TrashSyncService { final AppSettingsService _appSettingsService; - final RemoteAssetRepository _remoteAssetRepository; + final NativeSyncApi _nativeSyncApi; final DriftLocalAssetRepository _localAssetRepository; + final DriftLocalAlbumRepository _localAlbumRepository; + final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository; final LocalFilesManagerRepository _localFilesManager; final StorageRepository _storageRepository; final Platform _platform; @@ -21,85 +26,96 @@ class TrashSyncService { TrashSyncService({ required AppSettingsService appSettingsService, - required RemoteAssetRepository remoteAssetRepository, + required NativeSyncApi nativeSyncApi, required DriftLocalAssetRepository localAssetRepository, + required DriftLocalAlbumRepository localAlbumRepository, + required DriftTrashedLocalAssetRepository trashedLocalAssetRepository, required LocalFilesManagerRepository localFilesManager, required StorageRepository storageRepository, }) : _appSettingsService = appSettingsService, - _remoteAssetRepository = remoteAssetRepository, + _nativeSyncApi = nativeSyncApi, _localAssetRepository = localAssetRepository, + _localAlbumRepository = localAlbumRepository, + _trashedLocalAssetRepository = trashedLocalAssetRepository, _localFilesManager = localFilesManager, _storageRepository = storageRepository, _platform = const LocalPlatform(); - Future handleRemoteChanges(Iterable syncItems) async { - if (!_platform.isAndroid || !_appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { - return Future.value(); + bool get isAutoSyncMode => + _platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid); + + Future updateChecksums(Iterable assets) async => + _trashedLocalAssetRepository.updateChecksums(assets); + + Future> getAssetsToHash(String albumId) async => + _trashedLocalAssetRepository.getToHash(albumId); + + Future updateLocalTrashFromDevice() async { + final backupAlbums = await _localAlbumRepository.getBackupAlbums(); + if (backupAlbums.isEmpty) { + _logger.info("No backup albums found"); + return; } - final trashedAssetsItems = []; - final modifiedAssetsChecksums = {}; - for (var syncItem in syncItems) { - if (syncItem.deletedAt != null) { - trashedAssetsItems.add(syncItem); - } else { - modifiedAssetsChecksums.add(syncItem.checksum); - } + for (final album in backupAlbums) { + _logger.info("deviceTrashedAssets prepare, album: ${album.id}/${album.name}"); + final deviceTrashedAssets = await _nativeSyncApi.getTrashedAssetsForAlbum(album.id); + await _trashedLocalAssetRepository.applyTrashSnapshot(deviceTrashedAssets.toTrashedAssets(album.id), album.id); } - await _applyRemoteTrashToLocal(trashedAssetsItems); - await _applyRemoteRestoreToLocal(modifiedAssetsChecksums); + // todo find for more suitable place + await applyRemoteRestoreToLocal(); } - Future _applyRemoteTrashToLocal(Iterable trashedAssets) async { - if (trashedAssets.isEmpty) { + Future handleRemoteChanges(Iterable checksums) async { + if (checksums.isEmpty) { return Future.value(); } else { - final trashedAssetsMap = {for (final e in trashedAssets) e.checksum: e.remoteId}; - final localAssetsToTrash = await _localAssetRepository.getBackupSelectedAssets(trashedAssetsMap.keys); + final localAssetsToTrash = await _localAssetRepository.getBackupSelectedAssetsByAlbum(checksums); if (localAssetsToTrash.isNotEmpty) { final mediaUrls = await Future.wait( - localAssetsToTrash.map( - (localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl()), - ), + localAssetsToTrash.values + .expand((e) => e) + .map((localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl())), ); _logger.info("Moving to trash ${mediaUrls.join(", ")} assets"); - await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); - final itemsToTrash = []; - for (final asset in localAssetsToTrash) { - final remoteId = trashedAssetsMap[asset.checksum]!; - itemsToTrash.add((localId: asset.id, remoteId: remoteId)); + final result = await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); + if (result) { + await _localAssetRepository.trash(localAssetsToTrash); } - await _localAssetRepository.trash(itemsToTrash); } else { - _logger.info("No assets found in backup-enabled albums for assets: $trashedAssetsMap"); + _logger.info("No assets found in backup-enabled albums for assets: $checksums"); } } } - Future _applyRemoteRestoreToLocal(Iterable modifiedAssetsChecksums) async { - if (modifiedAssetsChecksums.isEmpty) { - return Future.value(); - } else { - final remoteAssetsToRestore = await _remoteAssetRepository.getByChecksums( - modifiedAssetsChecksums, - isTrashed: true, - ); - if (remoteAssetsToRestore.isNotEmpty) { - final remoteAssetMap = {for (final e in remoteAssetsToRestore) e.id: e.type}; - _logger.info("remoteAssetsToRestore: $remoteAssetMap"); - final localTrashedAssets = await _localAssetRepository.getLocalTrashedAssets(remoteAssetMap.keys); - if (localTrashedAssets.isNotEmpty) { - for (final LocalTrashedAsset asset in localTrashedAssets) { - _logger.info("Restoring from trash, localId: ${asset.localId}, remoteId: ${asset.remoteId}"); - final type = remoteAssetMap[asset.remoteId]!; - await _localFilesManager.restoreFromTrashById(asset.localId, type.index); - await _localAssetRepository.deleteLocalTrashedAssets(remoteAssetMap.keys); - } - } else { - _logger.info("No local assets found for restoration"); - } - } else { - _logger.info("No remote assets found for restoration"); + Future applyRemoteRestoreToLocal() async { + final remoteAssetsToRestore = await _trashedLocalAssetRepository.getToRestore(); + if (remoteAssetsToRestore.isNotEmpty) { + _logger.info("remoteAssetsToRestore: $remoteAssetsToRestore"); + for (final asset in remoteAssetsToRestore) { + _logger.info("Restoring from trash, localId: ${asset.id}, remoteId: ${asset.checksum}"); + await _localFilesManager.restoreFromTrashById(asset.id, asset.type.index); } + // todo It`s necessary? could cause race with deletion in applyTrashSnapshot? 18/09/2025 + await _trashedLocalAssetRepository.delete(remoteAssetsToRestore.map((e) => e.id)); + } else { + _logger.info("No remote assets found for restoration"); } } } + +extension on Iterable { + List toTrashedAssets(String albumId) { + return map( + (e) => TrashedAsset( + id: e.id, + name: e.name, + checksum: null, + type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other, + createdAt: tryFromSecondsSinceEpoch(e.createdAt) ?? DateTime.now(), + updatedAt: tryFromSecondsSinceEpoch(e.updatedAt) ?? DateTime.now(), + size: e.size, + albumId: albumId, + ), + ).toList(); + } +} diff --git a/mobile/lib/infrastructure/entities/local_trashed_asset.entity.dart b/mobile/lib/infrastructure/entities/local_trashed_asset.entity.dart deleted file mode 100644 index 678c7edfad..0000000000 --- a/mobile/lib/infrastructure/entities/local_trashed_asset.entity.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/local_trashed_asset.model.dart'; -import 'package:immich_mobile/infrastructure/entities/local_trashed_asset.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; -import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; - -@TableIndex.sql( - 'CREATE INDEX IF NOT EXISTS idx_local_trashed_asset_remote_id ON local_trashed_asset_entity (remote_id)', -) -class LocalTrashedAssetEntity extends Table with DriftDefaultsMixin { - const LocalTrashedAssetEntity(); - - TextColumn get id => text()(); - - TextColumn get remoteId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); - - DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); - - @override - Set get primaryKey => {id}; -} - -extension LocalTrashedAssetEntityDataDomainExtension on LocalTrashedAssetEntityData { - LocalTrashedAsset toDto() => LocalTrashedAsset(localId: id, remoteId: remoteId, createdAt: createdAt); -} diff --git a/mobile/lib/infrastructure/entities/local_trashed_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_trashed_asset.entity.drift.dart deleted file mode 100644 index 069ada9d59..0000000000 --- a/mobile/lib/infrastructure/entities/local_trashed_asset.entity.drift.dart +++ /dev/null @@ -1,614 +0,0 @@ -// dart format width=80 -// ignore_for_file: type=lint -import 'package:drift/drift.dart' as i0; -import 'package:immich_mobile/infrastructure/entities/local_trashed_asset.entity.drift.dart' - as i1; -import 'package:immich_mobile/infrastructure/entities/local_trashed_asset.entity.dart' - as i2; -import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' - as i4; -import 'package:drift/internal/modular.dart' as i5; - -typedef $$LocalTrashedAssetEntityTableCreateCompanionBuilder = - i1.LocalTrashedAssetEntityCompanion Function({ - required String id, - required String remoteId, - i0.Value createdAt, - }); -typedef $$LocalTrashedAssetEntityTableUpdateCompanionBuilder = - i1.LocalTrashedAssetEntityCompanion Function({ - i0.Value id, - i0.Value remoteId, - i0.Value createdAt, - }); - -final class $$LocalTrashedAssetEntityTableReferences - extends - i0.BaseReferences< - i0.GeneratedDatabase, - i1.$LocalTrashedAssetEntityTable, - i1.LocalTrashedAssetEntityData - > { - $$LocalTrashedAssetEntityTableReferences( - super.$_db, - super.$_table, - super.$_typedResult, - ); - - static i4.$RemoteAssetEntityTable _remoteIdTable(i0.GeneratedDatabase db) => - i5.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .createAlias( - i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet( - 'local_trashed_asset_entity', - ) - .remoteId, - i5.ReadDatabaseContainer( - db, - ).resultSet('remote_asset_entity').id, - ), - ); - - i4.$$RemoteAssetEntityTableProcessedTableManager get remoteId { - final $_column = $_itemColumn('remote_id')!; - - final manager = i4 - .$$RemoteAssetEntityTableTableManager( - $_db, - i5.ReadDatabaseContainer( - $_db, - ).resultSet('remote_asset_entity'), - ) - .filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_remoteIdTable($_db)); - if (item == null) return manager; - return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item]), - ); - } -} - -class $$LocalTrashedAssetEntityTableFilterComposer - extends - i0.Composer { - $$LocalTrashedAssetEntityTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - i0.ColumnFilters get id => $composableBuilder( - column: $table.id, - builder: (column) => i0.ColumnFilters(column), - ); - - i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnFilters(column), - ); - - i4.$$RemoteAssetEntityTableFilterComposer get remoteId { - final i4.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.remoteId, - referencedTable: i5.ReadDatabaseContainer( - $db, - ).resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => i4.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer( - $db, - ).resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$LocalTrashedAssetEntityTableOrderingComposer - extends - i0.Composer { - $$LocalTrashedAssetEntityTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, - builder: (column) => i0.ColumnOrderings(column), - ); - - i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column), - ); - - i4.$$RemoteAssetEntityTableOrderingComposer get remoteId { - final i4.$$RemoteAssetEntityTableOrderingComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.remoteId, - referencedTable: i5.ReadDatabaseContainer( - $db, - ).resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => i4.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer( - $db, - ).resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$LocalTrashedAssetEntityTableAnnotationComposer - extends - i0.Composer { - $$LocalTrashedAssetEntityTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - i0.GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - i0.GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - i4.$$RemoteAssetEntityTableAnnotationComposer get remoteId { - final i4.$$RemoteAssetEntityTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.remoteId, - referencedTable: i5.ReadDatabaseContainer( - $db, - ).resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => i4.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer( - $db, - ).resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$LocalTrashedAssetEntityTableTableManager - extends - i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalTrashedAssetEntityTable, - i1.LocalTrashedAssetEntityData, - i1.$$LocalTrashedAssetEntityTableFilterComposer, - i1.$$LocalTrashedAssetEntityTableOrderingComposer, - i1.$$LocalTrashedAssetEntityTableAnnotationComposer, - $$LocalTrashedAssetEntityTableCreateCompanionBuilder, - $$LocalTrashedAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalTrashedAssetEntityData, - i1.$$LocalTrashedAssetEntityTableReferences, - ), - i1.LocalTrashedAssetEntityData, - i0.PrefetchHooks Function({bool remoteId}) - > { - $$LocalTrashedAssetEntityTableTableManager( - i0.GeneratedDatabase db, - i1.$LocalTrashedAssetEntityTable table, - ) : super( - i0.TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - i1.$$LocalTrashedAssetEntityTableFilterComposer( - $db: db, - $table: table, - ), - createOrderingComposer: () => - i1.$$LocalTrashedAssetEntityTableOrderingComposer( - $db: db, - $table: table, - ), - createComputedFieldComposer: () => - i1.$$LocalTrashedAssetEntityTableAnnotationComposer( - $db: db, - $table: table, - ), - updateCompanionCallback: - ({ - i0.Value id = const i0.Value.absent(), - i0.Value remoteId = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - }) => i1.LocalTrashedAssetEntityCompanion( - id: id, - remoteId: remoteId, - createdAt: createdAt, - ), - createCompanionCallback: - ({ - required String id, - required String remoteId, - i0.Value createdAt = const i0.Value.absent(), - }) => i1.LocalTrashedAssetEntityCompanion.insert( - id: id, - remoteId: remoteId, - createdAt: createdAt, - ), - withReferenceMapper: (p0) => p0 - .map( - (e) => ( - e.readTable(table), - i1.$$LocalTrashedAssetEntityTableReferences(db, table, e), - ), - ) - .toList(), - prefetchHooksCallback: ({remoteId = false}) { - return i0.PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: - < - T extends i0.TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic - > - >(state) { - if (remoteId) { - state = - state.withJoin( - currentTable: table, - currentColumn: table.remoteId, - referencedTable: i1 - .$$LocalTrashedAssetEntityTableReferences - ._remoteIdTable(db), - referencedColumn: i1 - .$$LocalTrashedAssetEntityTableReferences - ._remoteIdTable(db) - .id, - ) - as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - ), - ); -} - -typedef $$LocalTrashedAssetEntityTableProcessedTableManager = - i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalTrashedAssetEntityTable, - i1.LocalTrashedAssetEntityData, - i1.$$LocalTrashedAssetEntityTableFilterComposer, - i1.$$LocalTrashedAssetEntityTableOrderingComposer, - i1.$$LocalTrashedAssetEntityTableAnnotationComposer, - $$LocalTrashedAssetEntityTableCreateCompanionBuilder, - $$LocalTrashedAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalTrashedAssetEntityData, - i1.$$LocalTrashedAssetEntityTableReferences, - ), - i1.LocalTrashedAssetEntityData, - i0.PrefetchHooks Function({bool remoteId}) - >; -i0.Index get idxLocalTrashedAssetRemoteId => i0.Index( - 'idx_local_trashed_asset_remote_id', - 'CREATE INDEX IF NOT EXISTS idx_local_trashed_asset_remote_id ON local_trashed_asset_entity (remote_id)', -); - -class $LocalTrashedAssetEntityTable extends i2.LocalTrashedAssetEntity - with - i0.TableInfo< - $LocalTrashedAssetEntityTable, - i1.LocalTrashedAssetEntityData - > { - @override - final i0.GeneratedDatabase attachedDatabase; - final String? _alias; - $LocalTrashedAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); - @override - late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', - aliasedName, - false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - ); - static const i0.VerificationMeta _remoteIdMeta = const i0.VerificationMeta( - 'remoteId', - ); - @override - late final i0.GeneratedColumn remoteId = i0.GeneratedColumn( - 'remote_id', - aliasedName, - false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', - ), - ); - static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( - 'createdAt', - ); - @override - late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn( - 'created_at', - aliasedName, - false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime, - ); - @override - List get $columns => [id, remoteId, createdAt]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'local_trashed_asset_entity'; - @override - i0.VerificationContext validateIntegrity( - i0.Insertable instance, { - bool isInserting = false, - }) { - final context = i0.VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } else if (isInserting) { - context.missing(_idMeta); - } - if (data.containsKey('remote_id')) { - context.handle( - _remoteIdMeta, - remoteId.isAcceptableOrUnknown(data['remote_id']!, _remoteIdMeta), - ); - } else if (isInserting) { - context.missing(_remoteIdMeta); - } - if (data.containsKey('created_at')) { - context.handle( - _createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), - ); - } - return context; - } - - @override - Set get $primaryKey => {id}; - @override - i1.LocalTrashedAssetEntityData map( - Map data, { - String? tablePrefix, - }) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return i1.LocalTrashedAssetEntityData( - id: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, - data['${effectivePrefix}id'], - )!, - remoteId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, - data['${effectivePrefix}remote_id'], - )!, - createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, - data['${effectivePrefix}created_at'], - )!, - ); - } - - @override - $LocalTrashedAssetEntityTable createAlias(String alias) { - return $LocalTrashedAssetEntityTable(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class LocalTrashedAssetEntityData extends i0.DataClass - implements i0.Insertable { - final String id; - final String remoteId; - final DateTime createdAt; - const LocalTrashedAssetEntityData({ - required this.id, - required this.remoteId, - required this.createdAt, - }); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = i0.Variable(id); - map['remote_id'] = i0.Variable(remoteId); - map['created_at'] = i0.Variable(createdAt); - return map; - } - - factory LocalTrashedAssetEntityData.fromJson( - Map json, { - i0.ValueSerializer? serializer, - }) { - serializer ??= i0.driftRuntimeOptions.defaultSerializer; - return LocalTrashedAssetEntityData( - id: serializer.fromJson(json['id']), - remoteId: serializer.fromJson(json['remoteId']), - createdAt: serializer.fromJson(json['createdAt']), - ); - } - @override - Map toJson({i0.ValueSerializer? serializer}) { - serializer ??= i0.driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'remoteId': serializer.toJson(remoteId), - 'createdAt': serializer.toJson(createdAt), - }; - } - - i1.LocalTrashedAssetEntityData copyWith({ - String? id, - String? remoteId, - DateTime? createdAt, - }) => i1.LocalTrashedAssetEntityData( - id: id ?? this.id, - remoteId: remoteId ?? this.remoteId, - createdAt: createdAt ?? this.createdAt, - ); - LocalTrashedAssetEntityData copyWithCompanion( - i1.LocalTrashedAssetEntityCompanion data, - ) { - return LocalTrashedAssetEntityData( - id: data.id.present ? data.id.value : this.id, - remoteId: data.remoteId.present ? data.remoteId.value : this.remoteId, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - ); - } - - @override - String toString() { - return (StringBuffer('LocalTrashedAssetEntityData(') - ..write('id: $id, ') - ..write('remoteId: $remoteId, ') - ..write('createdAt: $createdAt') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(id, remoteId, createdAt); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is i1.LocalTrashedAssetEntityData && - other.id == this.id && - other.remoteId == this.remoteId && - other.createdAt == this.createdAt); -} - -class LocalTrashedAssetEntityCompanion - extends i0.UpdateCompanion { - final i0.Value id; - final i0.Value remoteId; - final i0.Value createdAt; - const LocalTrashedAssetEntityCompanion({ - this.id = const i0.Value.absent(), - this.remoteId = const i0.Value.absent(), - this.createdAt = const i0.Value.absent(), - }); - LocalTrashedAssetEntityCompanion.insert({ - required String id, - required String remoteId, - this.createdAt = const i0.Value.absent(), - }) : id = i0.Value(id), - remoteId = i0.Value(remoteId); - static i0.Insertable custom({ - i0.Expression? id, - i0.Expression? remoteId, - i0.Expression? createdAt, - }) { - return i0.RawValuesInsertable({ - if (id != null) 'id': id, - if (remoteId != null) 'remote_id': remoteId, - if (createdAt != null) 'created_at': createdAt, - }); - } - - i1.LocalTrashedAssetEntityCompanion copyWith({ - i0.Value? id, - i0.Value? remoteId, - i0.Value? createdAt, - }) { - return i1.LocalTrashedAssetEntityCompanion( - id: id ?? this.id, - remoteId: remoteId ?? this.remoteId, - createdAt: createdAt ?? this.createdAt, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = i0.Variable(id.value); - } - if (remoteId.present) { - map['remote_id'] = i0.Variable(remoteId.value); - } - if (createdAt.present) { - map['created_at'] = i0.Variable(createdAt.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('LocalTrashedAssetEntityCompanion(') - ..write('id: $id, ') - ..write('remoteId: $remoteId, ') - ..write('createdAt: $createdAt') - ..write(')')) - .toString(); - } -} diff --git a/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart new file mode 100644 index 0000000000..d36dce9f35 --- /dev/null +++ b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart @@ -0,0 +1,42 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/asset/trashed_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)') +class TrashedLocalAssetEntity extends Table with DriftDefaultsMixin { + const TrashedLocalAssetEntity(); + + TextColumn get id => text()(); + + TextColumn get albumId => text()(); + + TextColumn get checksum => text().nullable()(); + + TextColumn get name => text()(); + + IntColumn get type => intEnum()(); + + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); + + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); + + IntColumn get size => integer().nullable()(); + + @override + Set get primaryKey => {id}; +} + +extension TrashedLocalAssetEntityDataDomainExtension on TrashedLocalAssetEntityData { + TrashedAsset toDto(String albumId) => TrashedAsset( + id: id, + name: name, + albumId: albumId, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + size: size, + ); +} diff --git a/mobile/lib/infrastructure/entities/trashed_local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.drift.dart new file mode 100644 index 0000000000..004be64ffc --- /dev/null +++ b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.drift.dart @@ -0,0 +1,794 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart' + as i1; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart' + as i3; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; + +typedef $$TrashedLocalAssetEntityTableCreateCompanionBuilder = + i1.TrashedLocalAssetEntityCompanion Function({ + required String id, + required String albumId, + i0.Value checksum, + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value size, + }); +typedef $$TrashedLocalAssetEntityTableUpdateCompanionBuilder = + i1.TrashedLocalAssetEntityCompanion Function({ + i0.Value id, + i0.Value albumId, + i0.Value checksum, + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value size, + }); + +class $$TrashedLocalAssetEntityTableFilterComposer + extends + i0.Composer { + $$TrashedLocalAssetEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get albumId => $composableBuilder( + column: $table.albumId, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get checksum => $composableBuilder( + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnWithTypeConverterFilters get type => + $composableBuilder( + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); + + i0.ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get size => $composableBuilder( + column: $table.size, + builder: (column) => i0.ColumnFilters(column), + ); +} + +class $$TrashedLocalAssetEntityTableOrderingComposer + extends + i0.Composer { + $$TrashedLocalAssetEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get albumId => $composableBuilder( + column: $table.albumId, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get checksum => $composableBuilder( + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get type => $composableBuilder( + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get size => $composableBuilder( + column: $table.size, + builder: (column) => i0.ColumnOrderings(column), + ); +} + +class $$TrashedLocalAssetEntityTableAnnotationComposer + extends + i0.Composer { + $$TrashedLocalAssetEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get albumId => + $composableBuilder(column: $table.albumId, builder: (column) => column); + + i0.GeneratedColumn get checksum => + $composableBuilder(column: $table.checksum, builder: (column) => column); + + i0.GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + i0.GeneratedColumnWithTypeConverter get type => + $composableBuilder(column: $table.type, builder: (column) => column); + + i0.GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + i0.GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); + + i0.GeneratedColumn get size => + $composableBuilder(column: $table.size, builder: (column) => column); +} + +class $$TrashedLocalAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$TrashedLocalAssetEntityTable, + i1.TrashedLocalAssetEntityData, + i1.$$TrashedLocalAssetEntityTableFilterComposer, + i1.$$TrashedLocalAssetEntityTableOrderingComposer, + i1.$$TrashedLocalAssetEntityTableAnnotationComposer, + $$TrashedLocalAssetEntityTableCreateCompanionBuilder, + $$TrashedLocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.TrashedLocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$TrashedLocalAssetEntityTable, + i1.TrashedLocalAssetEntityData + >, + ), + i1.TrashedLocalAssetEntityData, + i0.PrefetchHooks Function() + > { + $$TrashedLocalAssetEntityTableTableManager( + i0.GeneratedDatabase db, + i1.$TrashedLocalAssetEntityTable table, + ) : super( + i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$TrashedLocalAssetEntityTableFilterComposer( + $db: db, + $table: table, + ), + createOrderingComposer: () => + i1.$$TrashedLocalAssetEntityTableOrderingComposer( + $db: db, + $table: table, + ), + createComputedFieldComposer: () => + i1.$$TrashedLocalAssetEntityTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value size = const i0.Value.absent(), + }) => i1.TrashedLocalAssetEntityCompanion( + id: id, + albumId: albumId, + checksum: checksum, + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + size: size, + ), + createCompanionCallback: + ({ + required String id, + required String albumId, + i0.Value checksum = const i0.Value.absent(), + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value size = const i0.Value.absent(), + }) => i1.TrashedLocalAssetEntityCompanion.insert( + id: id, + albumId: albumId, + checksum: checksum, + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + size: size, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $$TrashedLocalAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$TrashedLocalAssetEntityTable, + i1.TrashedLocalAssetEntityData, + i1.$$TrashedLocalAssetEntityTableFilterComposer, + i1.$$TrashedLocalAssetEntityTableOrderingComposer, + i1.$$TrashedLocalAssetEntityTableAnnotationComposer, + $$TrashedLocalAssetEntityTableCreateCompanionBuilder, + $$TrashedLocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.TrashedLocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$TrashedLocalAssetEntityTable, + i1.TrashedLocalAssetEntityData + >, + ), + i1.TrashedLocalAssetEntityData, + i0.PrefetchHooks Function() + >; +i0.Index get idxTrashedLocalAssetChecksum => i0.Index( + 'idx_trashed_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)', +); + +class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity + with + i0.TableInfo< + $TrashedLocalAssetEntityTable, + i1.TrashedLocalAssetEntityData + > { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $TrashedLocalAssetEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); + @override + late final i0.GeneratedColumn albumId = i0.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); + @override + late final i0.GeneratedColumn checksum = i0.GeneratedColumn( + 'checksum', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); + @override + late final i0.GeneratedColumn name = i0.GeneratedColumn( + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + late final i0.GeneratedColumnWithTypeConverter type = + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$TrashedLocalAssetEntityTable.$convertertype, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); + @override + late final i0.GeneratedColumn createdAt = + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); + @override + late final i0.GeneratedColumn updatedAt = + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _sizeMeta = const i0.VerificationMeta( + 'size', + ); + @override + late final i0.GeneratedColumn size = i0.GeneratedColumn( + 'size', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + albumId, + checksum, + name, + type, + createdAt, + updatedAt, + size, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'trashed_local_asset_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, { + bool isInserting = false, + }) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('album_id')) { + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); + } else if (isInserting) { + context.missing(_albumIdMeta); + } + if (data.containsKey('checksum')) { + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('created_at')) { + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); + } + if (data.containsKey('updated_at')) { + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); + } + if (data.containsKey('size')) { + context.handle( + _sizeMeta, + size.isAcceptableOrUnknown(data['size']!, _sizeMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.TrashedLocalAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.TrashedLocalAssetEntityData( + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$TrashedLocalAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), + createdAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + size: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}size'], + ), + ); + } + + @override + $TrashedLocalAssetEntityTable createAlias(String alias) { + return $TrashedLocalAssetEntityTable(attachedDatabase, alias); + } + + static i0.JsonTypeConverter2 $convertertype = + const i0.EnumIndexConverter(i2.AssetType.values); + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class TrashedLocalAssetEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final String albumId; + final String? checksum; + final String name; + final i2.AssetType type; + final DateTime createdAt; + final DateTime updatedAt; + final int? size; + const TrashedLocalAssetEntityData({ + required this.id, + required this.albumId, + this.checksum, + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.size, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['album_id'] = i0.Variable(albumId); + if (!nullToAbsent || checksum != null) { + map['checksum'] = i0.Variable(checksum); + } + map['name'] = i0.Variable(name); + { + map['type'] = i0.Variable( + i1.$TrashedLocalAssetEntityTable.$convertertype.toSql(type), + ); + } + map['created_at'] = i0.Variable(createdAt); + map['updated_at'] = i0.Variable(updatedAt); + if (!nullToAbsent || size != null) { + map['size'] = i0.Variable(size); + } + return map; + } + + factory TrashedLocalAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return TrashedLocalAssetEntityData( + id: serializer.fromJson(json['id']), + albumId: serializer.fromJson(json['albumId']), + checksum: serializer.fromJson(json['checksum']), + name: serializer.fromJson(json['name']), + type: i1.$TrashedLocalAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + size: serializer.fromJson(json['size']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'albumId': serializer.toJson(albumId), + 'checksum': serializer.toJson(checksum), + 'name': serializer.toJson(name), + 'type': serializer.toJson( + i1.$TrashedLocalAssetEntityTable.$convertertype.toJson(type), + ), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'size': serializer.toJson(size), + }; + } + + i1.TrashedLocalAssetEntityData copyWith({ + String? id, + String? albumId, + i0.Value checksum = const i0.Value.absent(), + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value size = const i0.Value.absent(), + }) => i1.TrashedLocalAssetEntityData( + id: id ?? this.id, + albumId: albumId ?? this.albumId, + checksum: checksum.present ? checksum.value : this.checksum, + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + size: size.present ? size.value : this.size, + ); + TrashedLocalAssetEntityData copyWithCompanion( + i1.TrashedLocalAssetEntityCompanion data, + ) { + return TrashedLocalAssetEntityData( + id: data.id.present ? data.id.value : this.id, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + size: data.size.present ? data.size.value : this.size, + ); + } + + @override + String toString() { + return (StringBuffer('TrashedLocalAssetEntityData(') + ..write('id: $id, ') + ..write('albumId: $albumId, ') + ..write('checksum: $checksum, ') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('size: $size') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + albumId, + checksum, + name, + type, + createdAt, + updatedAt, + size, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.TrashedLocalAssetEntityData && + other.id == this.id && + other.albumId == this.albumId && + other.checksum == this.checksum && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.size == this.size); +} + +class TrashedLocalAssetEntityCompanion + extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value albumId; + final i0.Value checksum; + final i0.Value name; + final i0.Value type; + final i0.Value createdAt; + final i0.Value updatedAt; + final i0.Value size; + const TrashedLocalAssetEntityCompanion({ + this.id = const i0.Value.absent(), + this.albumId = const i0.Value.absent(), + this.checksum = const i0.Value.absent(), + this.name = const i0.Value.absent(), + this.type = const i0.Value.absent(), + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.size = const i0.Value.absent(), + }); + TrashedLocalAssetEntityCompanion.insert({ + required String id, + required String albumId, + this.checksum = const i0.Value.absent(), + required String name, + required i2.AssetType type, + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.size = const i0.Value.absent(), + }) : id = i0.Value(id), + albumId = i0.Value(albumId), + name = i0.Value(name), + type = i0.Value(type); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? albumId, + i0.Expression? checksum, + i0.Expression? name, + i0.Expression? type, + i0.Expression? createdAt, + i0.Expression? updatedAt, + i0.Expression? size, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (albumId != null) 'album_id': albumId, + if (checksum != null) 'checksum': checksum, + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (size != null) 'size': size, + }); + } + + i1.TrashedLocalAssetEntityCompanion copyWith({ + i0.Value? id, + i0.Value? albumId, + i0.Value? checksum, + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? size, + }) { + return i1.TrashedLocalAssetEntityCompanion( + id: id ?? this.id, + albumId: albumId ?? this.albumId, + checksum: checksum ?? this.checksum, + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + size: size ?? this.size, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (albumId.present) { + map['album_id'] = i0.Variable(albumId.value); + } + if (checksum.present) { + map['checksum'] = i0.Variable(checksum.value); + } + if (name.present) { + map['name'] = i0.Variable(name.value); + } + if (type.present) { + map['type'] = i0.Variable( + i1.$TrashedLocalAssetEntityTable.$convertertype.toSql(type.value), + ); + } + if (createdAt.present) { + map['created_at'] = i0.Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = i0.Variable(updatedAt.value); + } + if (size.present) { + map['size'] = i0.Variable(size.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TrashedLocalAssetEntityCompanion(') + ..write('id: $id, ') + ..write('albumId: $albumId, ') + ..write('checksum: $checksum, ') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('size: $size') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index fdffd884ee..0731a157a7 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -10,7 +10,7 @@ import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/local_trashed_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.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/partner.entity.dart'; @@ -63,7 +63,7 @@ class IsarDatabaseRepository implements IDatabaseRepository { PersonEntity, AssetFaceEntity, StoreEntity, - LocalTrashedAssetEntity, + TrashedLocalAssetEntity, ], include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, ) @@ -136,8 +136,8 @@ class Drift extends $Drift implements IDatabaseRepository { await m.alterTable(TableMigration(v10.userEntity)); }, from10To11: (m, v11) async { - await m.create(v11.localTrashedAssetEntity); - await m.createIndex(v11.idxLocalTrashedAssetRemoteId); + await m.create(v11.trashedLocalAssetEntity); + await m.createIndex(v11.idxTrashedLocalAssetChecksum); }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 108e0959b8..4edd8b2bf7 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -37,7 +37,7 @@ import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.da as i17; import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart' as i18; -import 'package:immich_mobile/infrastructure/entities/local_trashed_asset.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart' as i19; import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' as i20; @@ -79,8 +79,8 @@ abstract class $Drift extends i0.GeneratedDatabase { late final i17.$AssetFaceEntityTable assetFaceEntity = i17 .$AssetFaceEntityTable(this); late final i18.$StoreEntityTable storeEntity = i18.$StoreEntityTable(this); - late final i19.$LocalTrashedAssetEntityTable localTrashedAssetEntity = i19 - .$LocalTrashedAssetEntityTable(this); + late final i19.$TrashedLocalAssetEntityTable trashedLocalAssetEntity = i19 + .$TrashedLocalAssetEntityTable(this); i20.MergedAssetDrift get mergedAssetDrift => i21.ReadDatabaseContainer( this, ).accessor(i20.MergedAssetDrift.new); @@ -112,9 +112,9 @@ abstract class $Drift extends i0.GeneratedDatabase { personEntity, assetFaceEntity, storeEntity, - localTrashedAssetEntity, + trashedLocalAssetEntity, i11.idxLatLng, - i19.idxLocalTrashedAssetRemoteId, + i19.idxTrashedLocalAssetChecksum, ]; @override i0.StreamQueryUpdateRules @@ -294,18 +294,6 @@ abstract class $Drift extends i0.GeneratedDatabase { ), result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update)], ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName( - 'remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete, - ), - result: [ - i0.TableUpdate( - 'local_trashed_asset_entity', - kind: i0.UpdateKind.delete, - ), - ], - ), ]); @override i0.DriftDatabaseOptions get options => @@ -354,9 +342,9 @@ class $DriftManager { i17.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity); i18.$$StoreEntityTableTableManager get storeEntity => i18.$$StoreEntityTableTableManager(_db, _db.storeEntity); - i19.$$LocalTrashedAssetEntityTableTableManager get localTrashedAssetEntity => - i19.$$LocalTrashedAssetEntityTableTableManager( + i19.$$TrashedLocalAssetEntityTableTableManager get trashedLocalAssetEntity => + i19.$$TrashedLocalAssetEntityTableTableManager( _db, - _db.localTrashedAssetEntity, + _db.trashedLocalAssetEntity, ); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 16124137ff..4f3034fabf 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -4298,9 +4298,9 @@ final class Schema11 extends i0.VersionedSchema { personEntity, assetFaceEntity, storeEntity, - localTrashedAssetEntity, + trashedLocalAssetEntity, idxLatLng, - idxLocalTrashedAssetRemoteId, + idxTrashedLocalAssetChecksum, ]; late final Shape20 userEntity = Shape20( source: i0.VersionedTable( @@ -4645,13 +4645,22 @@ final class Schema11 extends i0.VersionedSchema { ), alias: null, ); - late final Shape22 localTrashedAssetEntity = Shape22( + late final Shape22 trashedLocalAssetEntity = Shape22( source: i0.VersionedTable( - entityName: 'local_trashed_asset_entity', + entityName: 'trashed_local_asset_entity', withoutRowId: true, isStrict: true, tableConstraints: ['PRIMARY KEY(id)'], - columns: [_column_0, _column_95, _column_9], + columns: [ + _column_0, + _column_95, + _column_22, + _column_1, + _column_8, + _column_9, + _column_5, + _column_96, + ], attachedDatabase: database, ), alias: null, @@ -4660,9 +4669,9 @@ final class Schema11 extends i0.VersionedSchema { 'idx_lat_lng', 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', ); - final i1.Index idxLocalTrashedAssetRemoteId = i1.Index( - 'idx_local_trashed_asset_remote_id', - 'CREATE INDEX IF NOT EXISTS idx_local_trashed_asset_remote_id ON local_trashed_asset_entity (remote_id)', + final i1.Index idxTrashedLocalAssetChecksum = i1.Index( + 'idx_trashed_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)', ); } @@ -4670,21 +4679,35 @@ class Shape22 extends i0.VersionedTable { Shape22({required super.source, required super.alias}) : super.aliased(); i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get remoteId => - columnsByName['remote_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get albumId => + columnsByName['album_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get size => + columnsByName['size']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_95(String aliasedName) => i1.GeneratedColumn( - 'remote_id', + 'album_id', aliasedName, false, type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', - ), + ); +i1.GeneratedColumn _column_96(String aliasedName) => + i1.GeneratedColumn( + 'size', + aliasedName, + true, + type: i1.DriftSqlType.int, ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 5efb8b17f5..4a5e01a76e 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -3,14 +3,12 @@ import 'package:drift/drift.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/infrastructure/entities/local_album.entity.dart'; -import 'package:immich_mobile/domain/models/local_trashed_asset.model.dart'; 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/local_trashed_asset.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/local_trashed_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -typedef LocalRemoteIds = ({String localId, String remoteId}); +typedef AssetsByAlbums = Map>; class DriftLocalAssetRepository extends DriftDatabaseRepository { final Drift _db; @@ -58,7 +56,7 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { }); } - Future delete(Iterable ids) { + Future delete(List ids) { if (ids.isEmpty) { return Future.value(); } @@ -70,27 +68,37 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { }); } - Future trash(Iterable ids) async { - if (ids.isEmpty) return; + Future trash(AssetsByAlbums assetsByAlbums) async { + if (assetsByAlbums.isEmpty) { + return; + } - final Map idToRemote = {for (final e in ids) e.localId: e.remoteId}; + final companions = []; + final idToDelete = {}; - final localRows = await (_db.localAssetEntity.select()..where((t) => t.id.isIn(idToRemote.keys))).get(); - - await _db.batch((batch) { - for (final row in localRows) { - final remoteId = idToRemote[row.id]; - if (remoteId == null) { - continue; - } - batch.insert( - _db.localTrashedAssetEntity, - LocalTrashedAssetEntityCompanion(id: Value(row.id), remoteId: Value(remoteId)), - mode: InsertMode.insertOrReplace, + assetsByAlbums.forEach((albumId, assets) { + for (final asset in assets) { + idToDelete.add(asset.id); + companions.add( + TrashedLocalAssetEntityCompanion( + id: Value(asset.id), + name: Value(asset.name), + albumId: Value(albumId), + checksum: asset.checksum == null ? const Value.absent() : Value(asset.checksum), + type: Value(asset.type), + ), ); } - for (final slice in idToRemote.keys.slices(32000)) { - batch.deleteWhere(_db.localAssetEntity, (e) => e.id.isIn(slice)); + }); + + await _db.transaction(() async { + for (final slice in companions.slices(200)) { + await _db.batch((batch) { + batch.insertAllOnConflictUpdate(_db.trashedLocalAssetEntity, slice); + }); + } + for (final slice in idToDelete.slices(800)) { + await (_db.delete(_db.localAssetEntity)..where((e) => e.id.isIn(slice))).go(); } }); } @@ -128,41 +136,30 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { return query.map((localAlbum) => localAlbum.toDto()).get(); } - Future> getBackupSelectedAssets(Iterable checksums) { + Future getBackupSelectedAssetsByAlbum(Iterable checksums) async { if (checksums.isEmpty) { - return Future.value([]); + return {}; } - final backedUpAssetIds = _db.localAlbumAssetEntity.selectOnly() - ..addColumns([_db.localAlbumAssetEntity.assetId]) - ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), - useColumns: false, - ), - ]) - ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected)); - final query = _db.localAssetEntity.select() - ..where((la) => la.checksum.isIn(checksums) & la.id.isInQuery(backedUpAssetIds)); - return query.map((row) => row.toDto()).get(); - } - Future> getLocalTrashedAssets(Iterable remoteIds) { - if (remoteIds.isEmpty) { - return Future.value([]); - } - final query = _db.localTrashedAssetEntity.select()..where((t) => t.remoteId.isIn(remoteIds)); - return query.map((row) => row.toDto()).get(); - } + final lAlbumAsset = _db.localAlbumAssetEntity; + final lAlbum = _db.localAlbumEntity; + final lAsset = _db.localAssetEntity; - Future deleteLocalTrashedAssets(Iterable remoteIds) { - if (remoteIds.isEmpty) { - return Future.value(); - } - return _db.batch((batch) { - for (final slice in remoteIds.slices(32000)) { - batch.deleteWhere(_db.localTrashedAssetEntity, (e) => e.remoteId.isIn(slice)); + final result = >{}; + + for (final slice in checksums.toSet().slices(800)) { + final rows = await (_db.select(lAlbumAsset).join([ + innerJoin(lAlbum, lAlbumAsset.albumId.equalsExp(lAlbum.id)), + innerJoin(lAsset, lAlbumAsset.assetId.equalsExp(lAsset.id)), + ])..where(lAlbum.backupSelection.equalsValue(BackupSelection.selected) & lAsset.checksum.isIn(slice))).get(); + + for (final row in rows) { + final albumId = row.readTable(lAlbumAsset).albumId; + final assetData = row.readTable(lAsset); + final asset = assetData.toDto(); + (result[albumId] ??= []).add(asset); } - }); + } + return result; } } diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 66eb4a20fa..6140c6af92 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -241,13 +241,4 @@ class RemoteAssetRepository extends DriftDatabaseRepository { Future getCount() { return _db.managers.remoteAssetEntity.count(); } - - Future> getByChecksums(Iterable checksums, {bool? isTrashed}) { - if (checksums.isEmpty) return Future.value([]); - final query = _db.remoteAssetEntity.select()..where((rae) => rae.checksum.isIn(checksums)); - if (isTrashed != null) { - query.where((rae) => isTrashed ? rae.deletedAt.isNotNull() : rae.deletedAt.isNull()); - } - return query.map((row) => row.toDto()).get(); - } } diff --git a/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart b/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart new file mode 100644 index 0000000000..b99d7f75ce --- /dev/null +++ b/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart @@ -0,0 +1,122 @@ +import 'package:collection/collection.dart'; +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/models/asset/trashed_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository { + final Drift _db; + + const DriftTrashedLocalAssetRepository(this._db) : super(_db); + + Future updateChecksums(Iterable assets) { + if (assets.isEmpty) { + return Future.value(); + } + + return _db.batch((batch) async { + for (final asset in assets) { + batch.update( + _db.trashedLocalAssetEntity, + TrashedLocalAssetEntityCompanion(checksum: Value(asset.checksum)), + where: (e) => e.id.equals(asset.id), + ); + } + }); + } + + Future> getToHash(String albumId) { + final query = _db.trashedLocalAssetEntity.select()..where((r) => r.albumId.equals(albumId) & r.checksum.isNull()); + return query.map((row) => row.toDto(albumId)).get(); + } + + Future> getToRestore() async { + final trashed = _db.trashedLocalAssetEntity; + final remote = _db.remoteAssetEntity; + final album = _db.localAlbumEntity; + + final selectedAlbumIds = (_db.selectOnly(album) + ..addColumns([album.id]) + ..where(album.backupSelection.equalsValue(BackupSelection.selected))); + + final rows = await (_db.select(trashed).join([ + innerJoin(remote, remote.checksum.equalsExp(trashed.checksum)), + ])..where(trashed.albumId.isInQuery(selectedAlbumIds) & remote.deletedAt.isNull())).get(); + + return rows.map((result) { + final assetData = result.readTable(trashed); + return assetData.toDto(assetData.albumId); + }).toList(); + } + + /// Applies resulted snapshot of trashed assets: + /// - upserts incoming rows + /// - deletes rows that are not present in the snapshot + Future applyTrashSnapshot(Iterable assets, String albumId) async { + if (assets.isEmpty) { + await _db.delete(_db.trashedLocalAssetEntity).go(); + return; + } + + return _db.transaction(() async { + final table = _db.trashedLocalAssetEntity; + + final companions = assets.map( + (a) => TrashedLocalAssetEntityCompanion.insert( + id: a.id, + albumId: albumId, + checksum: a.checksum == null ? const Value.absent() : Value(a.checksum), + name: a.name, + type: a.type, + createdAt: Value(a.createdAt), + updatedAt: Value(a.updatedAt), + size: a.size == null ? const Value.absent() : Value(a.size), + ), + ); + + for (final slice in companions.slices(400)) { + await _db.batch((b) { + b.insertAllOnConflictUpdate(table, slice); + }); + } + + final keepIds = assets.map((asset) => asset.id); + if (keepIds.length <= 900) { + await (_db.delete(table)..where((row) => row.id.isNotIn(keepIds))).go(); + } else { + final existingIds = await (_db.selectOnly(table)..addColumns([table.id])).map((r) => r.read(table.id)!).get(); + final toDelete = existingIds.where((id) => !keepIds.contains(id)); + for (final slice in toDelete.slices(400)) { + await (_db.delete(table)..where((row) => row.id.isIn(slice))).go(); + } + } + }); + } + + Stream watchCount() { + final t = _db.trashedLocalAssetEntity; + return (_db.selectOnly(t)..addColumns([t.id.count()])).watchSingle().map((row) => row.read(t.id.count()) ?? 0); + } + + Stream watchHashedCount() { + final t = _db.trashedLocalAssetEntity; + return (_db.selectOnly(t) + ..addColumns([t.id.count()]) + ..where(t.checksum.isNotNull())) + .watchSingle() + .map((row) => row.read(t.id.count()) ?? 0); + } + + Future delete(Iterable ids) { + if (ids.isEmpty) { + return Future.value(); + } + return _db.batch((batch) { + for (final slice in ids.slices(32000)) { + batch.deleteWhere(_db.trashedLocalAssetEntity, (e) => e.id.isIn(slice)); + } + }); + } +} diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index 6fc96f5046..afab28a0e2 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -41,6 +41,7 @@ class PlatformAsset { required this.durationInSeconds, required this.orientation, required this.isFavorite, + this.size, }); String id; @@ -63,8 +64,22 @@ class PlatformAsset { bool isFavorite; + int? size; + List _toList() { - return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite]; + return [ + id, + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + orientation, + isFavorite, + size, + ]; } Object encode() { @@ -84,6 +99,7 @@ class PlatformAsset { durationInSeconds: result[7]! as int, orientation: result[8]! as int, isFavorite: result[9]! as bool, + size: result[10] as int?, ); } @@ -205,6 +221,45 @@ class SyncDelta { int get hashCode => Object.hashAll(_toList()); } +class TrashedAssetParams { + TrashedAssetParams({required this.id, required this.type, this.albumId}); + + String id; + + int type; + + String? albumId; + + List _toList() { + return [id, type, albumId]; + } + + Object encode() { + return _toList(); + } + + static TrashedAssetParams decode(Object result) { + result as List; + return TrashedAssetParams(id: result[0]! as String, type: result[1]! as int, albumId: result[2] as String?); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! TrashedAssetParams || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -221,6 +276,9 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is SyncDelta) { buffer.putUint8(131); writeValue(buffer, value.encode()); + } else if (value is TrashedAssetParams) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -235,6 +293,8 @@ class _PigeonCodec extends StandardMessageCodec { return PlatformAlbum.decode(readValue(buffer)!); case 131: return SyncDelta.decode(readValue(buffer)!); + case 132: + return TrashedAssetParams.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -495,4 +555,60 @@ class NativeSyncApi { return (pigeonVar_replyList[0] as List?)!.cast(); } } + + Future> getTrashedAssetsForAlbum(String albumId, {int? updatedTimeCond}) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssetsForAlbum$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId, updatedTimeCond]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)!.cast(); + } + } + + Future> hashTrashedAssets(List trashedAssets) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashTrashedAssets$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([trashedAssets]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)!.cast(); + } + } } diff --git a/mobile/lib/providers/infrastructure/asset.provider.dart b/mobile/lib/providers/infrastructure/asset.provider.dart index 102e6aa60c..c3c0e9788d 100644 --- a/mobile/lib/providers/infrastructure/asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset.provider.dart @@ -2,6 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; final localAssetRepository = Provider( @@ -12,6 +13,10 @@ final remoteAssetRepositoryProvider = Provider( (ref) => RemoteAssetRepository(ref.watch(driftProvider)), ); +final trashedLocalAssetRepository = Provider( + (ref) => DriftTrashedLocalAssetRepository(ref.watch(driftProvider)), +); + final assetServiceProvider = Provider( (ref) => AssetService( remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider), diff --git a/mobile/lib/providers/infrastructure/sync.provider.dart b/mobile/lib/providers/infrastructure/sync.provider.dart index 20b832f0cc..cc8fcdd7d1 100644 --- a/mobile/lib/providers/infrastructure/sync.provider.dart +++ b/mobile/lib/providers/infrastructure/sync.provider.dart @@ -29,6 +29,7 @@ final syncStreamRepositoryProvider = Provider((ref) => SyncStreamRepository(ref. final localSyncServiceProvider = Provider( (ref) => LocalSyncService( localAlbumRepository: ref.watch(localAlbumRepository), + trashSyncService: ref.watch(trashSyncServiceProvider), nativeSyncApi: ref.watch(nativeSyncApiProvider), ), ); @@ -39,5 +40,6 @@ final hashServiceProvider = Provider( localAssetRepository: ref.watch(localAssetRepository), storageRepository: ref.watch(storageRepositoryProvider), nativeSyncApi: ref.watch(nativeSyncApiProvider), + trashSyncService: ref.watch(trashSyncServiceProvider), ), ); diff --git a/mobile/lib/providers/infrastructure/trash_sync.provider.dart b/mobile/lib/providers/infrastructure/trash_sync.provider.dart index df8a79f661..a77202a36b 100644 --- a/mobile/lib/providers/infrastructure/trash_sync.provider.dart +++ b/mobile/lib/providers/infrastructure/trash_sync.provider.dart @@ -1,17 +1,31 @@ +import 'package:async/async.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/trash_sync.service.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; import 'asset.provider.dart'; +typedef TrashedAssetsCount = ({int total, int hashed}); + final trashSyncServiceProvider = Provider( (ref) => TrashSyncService( appSettingsService: ref.watch(appSettingsServiceProvider), - remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider), + nativeSyncApi: ref.watch(nativeSyncApiProvider), localAssetRepository: ref.watch(localAssetRepository), + localAlbumRepository: ref.watch(localAlbumRepository), + trashedLocalAssetRepository: ref.watch(trashedLocalAssetRepository), localFilesManager: ref.watch(localFilesManagerRepositoryProvider), storageRepository: ref.watch(storageRepositoryProvider), ), ); + +final trashedAssetsCountProvider = StreamProvider((ref) { + final repo = ref.watch(trashedLocalAssetRepository); + final total$ = repo.watchCount(); + final hashed$ = repo.watchHashedCount(); + return StreamZip([total$, hashed$]).map((values) => (total: values[0], hashed: values[1])); +}); diff --git a/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart index 15c015736b..44f79d4733 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart @@ -5,11 +5,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/trash_sync.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/utils/migration.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; @@ -225,6 +226,7 @@ class _SyncStatsCounts extends ConsumerWidget { final localAlbumService = ref.watch(localAlbumServiceProvider); final remoteAlbumService = ref.watch(remoteAlbumServiceProvider); final memoryService = ref.watch(driftMemoryServiceProvider); + final trashSyncService = ref.watch(trashSyncServiceProvider); Future> loadCounts() async { final assetCounts = assetService.getAssetCounts(); @@ -347,6 +349,42 @@ class _SyncStatsCounts extends ConsumerWidget { ], ), ), + if (trashSyncService.isAutoSyncMode) ...[ + _SectionHeaderText(text: "trashed".t(context: context)), + Consumer( + builder: (context, ref, _) { + final counts = ref.watch(trashedAssetsCountProvider); + return counts.when( + data: (c) => Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: c.total, + icon: Icons.delete_outline, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "hashed_assets".t(context: context), + count: c.hashed, + icon: Icons.tag, + ), + ), + ], + ), + ), + loading: () => const CircularProgressIndicator(), + error: (e, st) => Text('Error: $e'), + ); + }, + ), + ], ], ); }, diff --git a/mobile/pigeon/native_sync_api.dart b/mobile/pigeon/native_sync_api.dart index e84c814c3d..33561433fd 100644 --- a/mobile/pigeon/native_sync_api.dart +++ b/mobile/pigeon/native_sync_api.dart @@ -24,6 +24,7 @@ class PlatformAsset { final int durationInSeconds; final int orientation; final bool isFavorite; + final int? size; const PlatformAsset({ required this.id, @@ -36,6 +37,7 @@ class PlatformAsset { this.durationInSeconds = 0, this.orientation = 0, this.isFavorite = false, + this.size, }); } @@ -71,6 +73,19 @@ class SyncDelta { }); } +class TrashedAssetParams { + final String id; + // Follows AssetType enum from base_asset.model.dart + final int type; + final String? albumId; + + const TrashedAssetParams({ + required this.id, + required this.type, + this.albumId, + }); +} + @HostApi() abstract class NativeSyncApi { bool shouldFullSync(); @@ -96,4 +111,11 @@ abstract class NativeSyncApi { @TaskQueue(type: TaskQueueType.serialBackgroundThread) List hashPaths(List paths); + + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + List getTrashedAssetsForAlbum(String albumId, {int? updatedTimeCond}); + + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + List hashTrashedAssets(List trashedAssets); + } diff --git a/mobile/test/domain/services/hash_service_test.dart b/mobile/test/domain/services/hash_service_test.dart index 7969131e7f..a8e437d499 100644 --- a/mobile/test/domain/services/hash_service_test.dart +++ b/mobile/test/domain/services/hash_service_test.dart @@ -21,6 +21,7 @@ void main() { late MockLocalAssetRepository mockAssetRepo; late MockStorageRepository mockStorageRepo; late MockNativeSyncApi mockNativeApi; + late MockTrashSyncService mockTrashSyncService; final sortBy = {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}; setUp(() { @@ -28,12 +29,14 @@ void main() { mockAssetRepo = MockLocalAssetRepository(); mockStorageRepo = MockStorageRepository(); mockNativeApi = MockNativeSyncApi(); + mockTrashSyncService = MockTrashSyncService(); sut = HashService( localAlbumRepository: mockAlbumRepo, localAssetRepository: mockAssetRepo, storageRepository: mockStorageRepo, nativeSyncApi: mockNativeApi, + trashSyncService: mockTrashSyncService, ); registerFallbackValue(LocalAlbumStub.recent); @@ -138,6 +141,7 @@ void main() { localAssetRepository: mockAssetRepo, storageRepository: mockStorageRepo, nativeSyncApi: mockNativeApi, + trashSyncService: mockTrashSyncService, batchFileLimit: 1, ); @@ -173,6 +177,7 @@ void main() { localAssetRepository: mockAssetRepo, storageRepository: mockStorageRepo, nativeSyncApi: mockNativeApi, + trashSyncService: mockTrashSyncService, batchSizeLimit: 80, ); diff --git a/mobile/test/drift/main/generated/schema_v11.dart b/mobile/test/drift/main/generated/schema_v11.dart index 8c2127479a..a4111031f4 100644 --- a/mobile/test/drift/main/generated/schema_v11.dart +++ b/mobile/test/drift/main/generated/schema_v11.dart @@ -7074,12 +7074,12 @@ class StoreEntityCompanion extends UpdateCompanion { } } -class LocalTrashedAssetEntity extends Table - with TableInfo { +class TrashedLocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; - LocalTrashedAssetEntity(this.attachedDatabase, [this._alias]); + TrashedLocalAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( 'id', aliasedName, @@ -7087,15 +7087,33 @@ class LocalTrashedAssetEntity extends Table type: DriftSqlType.string, requiredDuringInsert: true, ); - late final GeneratedColumn remoteId = GeneratedColumn( - 'remote_id', + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', - ), + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, ); late final GeneratedColumn createdAt = GeneratedColumn( 'created_at', @@ -7105,40 +7123,84 @@ class LocalTrashedAssetEntity extends Table requiredDuringInsert: false, defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn size = GeneratedColumn( + 'size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); @override - List get $columns => [id, remoteId, createdAt]; + List get $columns => [ + id, + albumId, + checksum, + name, + type, + createdAt, + updatedAt, + size, + ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; - static const String $name = 'local_trashed_asset_entity'; + static const String $name = 'trashed_local_asset_entity'; @override Set get $primaryKey => {id}; @override - LocalTrashedAssetEntityData map( + TrashedLocalAssetEntityData map( Map data, { String? tablePrefix, }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return LocalTrashedAssetEntityData( + return TrashedLocalAssetEntityData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], )!, - remoteId: attachedDatabase.typeMapping.read( + albumId: attachedDatabase.typeMapping.read( DriftSqlType.string, - data['${effectivePrefix}remote_id'], + data['${effectivePrefix}album_id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], )!, createdAt: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}created_at'], )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + size: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}size'], + ), ); } @override - LocalTrashedAssetEntity createAlias(String alias) { - return LocalTrashedAssetEntity(attachedDatabase, alias); + TrashedLocalAssetEntity createAlias(String alias) { + return TrashedLocalAssetEntity(attachedDatabase, alias); } @override @@ -7147,34 +7209,58 @@ class LocalTrashedAssetEntity extends Table bool get isStrict => true; } -class LocalTrashedAssetEntityData extends DataClass - implements Insertable { +class TrashedLocalAssetEntityData extends DataClass + implements Insertable { final String id; - final String remoteId; + final String albumId; + final String? checksum; + final String name; + final int type; final DateTime createdAt; - const LocalTrashedAssetEntityData({ + final DateTime updatedAt; + final int? size; + const TrashedLocalAssetEntityData({ required this.id, - required this.remoteId, + required this.albumId, + this.checksum, + required this.name, + required this.type, required this.createdAt, + required this.updatedAt, + this.size, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); - map['remote_id'] = Variable(remoteId); + map['album_id'] = Variable(albumId); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['name'] = Variable(name); + map['type'] = Variable(type); map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || size != null) { + map['size'] = Variable(size); + } return map; } - factory LocalTrashedAssetEntityData.fromJson( + factory TrashedLocalAssetEntityData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; - return LocalTrashedAssetEntityData( + return TrashedLocalAssetEntityData( id: serializer.fromJson(json['id']), - remoteId: serializer.fromJson(json['remoteId']), + albumId: serializer.fromJson(json['albumId']), + checksum: serializer.fromJson(json['checksum']), + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + size: serializer.fromJson(json['size']), ); } @override @@ -7182,88 +7268,164 @@ class LocalTrashedAssetEntityData extends DataClass serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), - 'remoteId': serializer.toJson(remoteId), + 'albumId': serializer.toJson(albumId), + 'checksum': serializer.toJson(checksum), + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'size': serializer.toJson(size), }; } - LocalTrashedAssetEntityData copyWith({ + TrashedLocalAssetEntityData copyWith({ String? id, - String? remoteId, + String? albumId, + Value checksum = const Value.absent(), + String? name, + int? type, DateTime? createdAt, - }) => LocalTrashedAssetEntityData( + DateTime? updatedAt, + Value size = const Value.absent(), + }) => TrashedLocalAssetEntityData( id: id ?? this.id, - remoteId: remoteId ?? this.remoteId, + albumId: albumId ?? this.albumId, + checksum: checksum.present ? checksum.value : this.checksum, + name: name ?? this.name, + type: type ?? this.type, createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + size: size.present ? size.value : this.size, ); - LocalTrashedAssetEntityData copyWithCompanion( - LocalTrashedAssetEntityCompanion data, + TrashedLocalAssetEntityData copyWithCompanion( + TrashedLocalAssetEntityCompanion data, ) { - return LocalTrashedAssetEntityData( + return TrashedLocalAssetEntityData( id: data.id.present ? data.id.value : this.id, - remoteId: data.remoteId.present ? data.remoteId.value : this.remoteId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + size: data.size.present ? data.size.value : this.size, ); } @override String toString() { - return (StringBuffer('LocalTrashedAssetEntityData(') + return (StringBuffer('TrashedLocalAssetEntityData(') ..write('id: $id, ') - ..write('remoteId: $remoteId, ') - ..write('createdAt: $createdAt') + ..write('albumId: $albumId, ') + ..write('checksum: $checksum, ') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('size: $size') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, remoteId, createdAt); + int get hashCode => Object.hash( + id, + albumId, + checksum, + name, + type, + createdAt, + updatedAt, + size, + ); @override bool operator ==(Object other) => identical(this, other) || - (other is LocalTrashedAssetEntityData && + (other is TrashedLocalAssetEntityData && other.id == this.id && - other.remoteId == this.remoteId && - other.createdAt == this.createdAt); + other.albumId == this.albumId && + other.checksum == this.checksum && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.size == this.size); } -class LocalTrashedAssetEntityCompanion - extends UpdateCompanion { +class TrashedLocalAssetEntityCompanion + extends UpdateCompanion { final Value id; - final Value remoteId; + final Value albumId; + final Value checksum; + final Value name; + final Value type; final Value createdAt; - const LocalTrashedAssetEntityCompanion({ + final Value updatedAt; + final Value size; + const TrashedLocalAssetEntityCompanion({ this.id = const Value.absent(), - this.remoteId = const Value.absent(), + this.albumId = const Value.absent(), + this.checksum = const Value.absent(), + this.name = const Value.absent(), + this.type = const Value.absent(), this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.size = const Value.absent(), }); - LocalTrashedAssetEntityCompanion.insert({ + TrashedLocalAssetEntityCompanion.insert({ required String id, - required String remoteId, + required String albumId, + this.checksum = const Value.absent(), + required String name, + required int type, this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.size = const Value.absent(), }) : id = Value(id), - remoteId = Value(remoteId); - static Insertable custom({ + albumId = Value(albumId), + name = Value(name), + type = Value(type); + static Insertable custom({ Expression? id, - Expression? remoteId, + Expression? albumId, + Expression? checksum, + Expression? name, + Expression? type, Expression? createdAt, + Expression? updatedAt, + Expression? size, }) { return RawValuesInsertable({ if (id != null) 'id': id, - if (remoteId != null) 'remote_id': remoteId, + if (albumId != null) 'album_id': albumId, + if (checksum != null) 'checksum': checksum, + if (name != null) 'name': name, + if (type != null) 'type': type, if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (size != null) 'size': size, }); } - LocalTrashedAssetEntityCompanion copyWith({ + TrashedLocalAssetEntityCompanion copyWith({ Value? id, - Value? remoteId, + Value? albumId, + Value? checksum, + Value? name, + Value? type, Value? createdAt, + Value? updatedAt, + Value? size, }) { - return LocalTrashedAssetEntityCompanion( + return TrashedLocalAssetEntityCompanion( id: id ?? this.id, - remoteId: remoteId ?? this.remoteId, + albumId: albumId ?? this.albumId, + checksum: checksum ?? this.checksum, + name: name ?? this.name, + type: type ?? this.type, createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + size: size ?? this.size, ); } @@ -7273,21 +7435,41 @@ class LocalTrashedAssetEntityCompanion if (id.present) { map['id'] = Variable(id.value); } - if (remoteId.present) { - map['remote_id'] = Variable(remoteId.value); + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (size.present) { + map['size'] = Variable(size.value); + } return map; } @override String toString() { - return (StringBuffer('LocalTrashedAssetEntityCompanion(') + return (StringBuffer('TrashedLocalAssetEntityCompanion(') ..write('id: $id, ') - ..write('remoteId: $remoteId, ') - ..write('createdAt: $createdAt') + ..write('albumId: $albumId, ') + ..write('checksum: $checksum, ') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('size: $size') ..write(')')) .toString(); } @@ -7336,15 +7518,15 @@ class DatabaseAtV11 extends GeneratedDatabase { late final PersonEntity personEntity = PersonEntity(this); late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); late final StoreEntity storeEntity = StoreEntity(this); - late final LocalTrashedAssetEntity localTrashedAssetEntity = - LocalTrashedAssetEntity(this); + late final TrashedLocalAssetEntity trashedLocalAssetEntity = + TrashedLocalAssetEntity(this); late final Index idxLatLng = Index( 'idx_lat_lng', 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', ); - late final Index idxLocalTrashedAssetRemoteId = Index( - 'idx_local_trashed_asset_remote_id', - 'CREATE INDEX IF NOT EXISTS idx_local_trashed_asset_remote_id ON local_trashed_asset_entity (remote_id)', + late final Index idxTrashedLocalAssetChecksum = Index( + 'idx_trashed_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)', ); @override Iterable> get allTables => @@ -7374,9 +7556,9 @@ class DatabaseAtV11 extends GeneratedDatabase { personEntity, assetFaceEntity, storeEntity, - localTrashedAssetEntity, + trashedLocalAssetEntity, idxLatLng, - idxLocalTrashedAssetRemoteId, + idxTrashedLocalAssetChecksum, ]; @override int get schemaVersion => 11;