mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
feat: native sync android
This commit is contained in:
parent
214893d2f4
commit
c0acd863cf
@ -3,6 +3,7 @@ plugins {
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
id 'com.google.devtools.ksp'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization'
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
@ -89,6 +90,7 @@ dependencies {
|
||||
def concurrent_version = '1.2.0'
|
||||
def guava_version = '33.3.1-android'
|
||||
def glide_version = '4.16.0'
|
||||
def serialization_version = '1.8.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
@ -96,6 +98,7 @@ dependencies {
|
||||
implementation "androidx.concurrent:concurrent-futures:$concurrent_version"
|
||||
implementation "com.google.guava:guava:$guava_version"
|
||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
|
||||
ksp "com.github.bumptech.glide:ksp:$glide_version"
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2'
|
||||
}
|
||||
|
@ -2,12 +2,12 @@ package app.alextran.immich
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import androidx.annotation.NonNull
|
||||
import app.alextran.immich.platform.MessagesImpl
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
flutterEngine.plugins.add(BackgroundServicePlugin())
|
||||
// No need to set up method channel here as it's now handled in the plugin
|
||||
ImHostService.setUp(flutterEngine.dartExecutor.binaryMessenger, MessagesImpl(this))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,238 @@
|
||||
package app.alextran.immich.platform
|
||||
|
||||
import Asset
|
||||
import SyncDelta
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.provider.MediaStore.VOLUME_EXTERNAL
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.RequiresExtension
|
||||
import androidx.core.content.edit
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
class MediaManager(context: Context) {
|
||||
private val ctx: Context = context.applicationContext
|
||||
|
||||
companion object {
|
||||
private const val SHARED_PREF_NAME = "Immich::MediaManager"
|
||||
private const val SHARED_PREF_MEDIA_STORE_VERSION_KEY = "MediaStore::getVersion"
|
||||
private const val SHARED_PREF_MEDIA_STORE_GEN_KEY = "MediaStore::getGeneration"
|
||||
|
||||
private fun getSavedGenerationMap(context: Context): Map<String, Long> {
|
||||
return context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getString(SHARED_PREF_MEDIA_STORE_GEN_KEY, null)?.let {
|
||||
Json.decodeFromString<Map<String, Long>>(it)
|
||||
} ?: emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
fun shouldFullSync(callback: (Result<Boolean>) -> Unit) {
|
||||
val currVersion = MediaStore.getVersion(ctx)
|
||||
val lastVersion = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, null)
|
||||
callback(Result.success(currVersion != lastVersion))
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
@RequiresExtension(extension = Build.VERSION_CODES.R, version = 1)
|
||||
fun hasMediaChanges(callback: (Result<Boolean>) -> Unit) {
|
||||
val genMap = getSavedGenerationMap(ctx)
|
||||
val currentVolumes = MediaStore.getExternalVolumeNames(ctx)
|
||||
if (currentVolumes != genMap.keys) {
|
||||
callback(Result.success(true))
|
||||
return
|
||||
}
|
||||
|
||||
val hasChanged = currentVolumes.any { volume ->
|
||||
val currentGen = MediaStore.getGeneration(ctx, volume)
|
||||
currentGen != genMap[volume]
|
||||
}
|
||||
callback(Result.success(hasChanged))
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
@RequiresExtension(extension = Build.VERSION_CODES.R, version = 1)
|
||||
fun checkpointSync(callback: (Result<Unit>) -> Unit) {
|
||||
val genMap = MediaStore.getExternalVolumeNames(ctx).associateWith {
|
||||
MediaStore.getGeneration(ctx, it)
|
||||
}.let { Json.encodeToString(it) }
|
||||
|
||||
ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit {
|
||||
putString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, MediaStore.getVersion(ctx))
|
||||
putString(SHARED_PREF_MEDIA_STORE_GEN_KEY, genMap)
|
||||
}
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
fun getAssetIdsForAlbum(
|
||||
albumId: String,
|
||||
callback: (Result<List<String>>) -> Unit
|
||||
) {
|
||||
try {
|
||||
val currentIds = mutableListOf<String>()
|
||||
val uri = MediaStore.Files.getContentUri(VOLUME_EXTERNAL)
|
||||
val projection = arrayOf(MediaStore.Files.FileColumns._ID)
|
||||
val selection = """
|
||||
${MediaStore.Files.FileColumns.BUCKET_ID} = ?
|
||||
AND (
|
||||
${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?
|
||||
OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?
|
||||
)
|
||||
""".trimIndent()
|
||||
val selectionArgs = arrayOf(
|
||||
albumId,
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString()
|
||||
)
|
||||
val sortOrder = null
|
||||
|
||||
ctx.contentResolver.query(
|
||||
uri,
|
||||
projection,
|
||||
selection,
|
||||
selectionArgs,
|
||||
sortOrder
|
||||
)?.use { cursor ->
|
||||
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
|
||||
while (cursor.moveToNext()) {
|
||||
currentIds.add(cursor.getLong(idColumn).toString())
|
||||
}
|
||||
}
|
||||
|
||||
callback(Result.success(currentIds))
|
||||
} catch (e: Exception) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
@RequiresExtension(extension = Build.VERSION_CODES.R, version = 1)
|
||||
fun getMediaChanges(callback: (Result<SyncDelta>) -> Unit) {
|
||||
try {
|
||||
val genMap = getSavedGenerationMap(ctx)
|
||||
val currentVolumes = MediaStore.getExternalVolumeNames(ctx)
|
||||
val changed = mutableListOf<Asset>()
|
||||
val deleted = mutableListOf<String>()
|
||||
|
||||
for (volume in currentVolumes) {
|
||||
val currentGen = MediaStore.getGeneration(ctx, volume)
|
||||
val storedGen = genMap[volume]
|
||||
|
||||
if (storedGen != null && currentGen < storedGen) {
|
||||
continue
|
||||
}
|
||||
|
||||
val queryUri = MediaStore.Files.getContentUri(volume)
|
||||
val projection = arrayOf(
|
||||
MediaStore.MediaColumns._ID,
|
||||
MediaStore.MediaColumns.DATA,
|
||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||
MediaStore.MediaColumns.TITLE,
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE,
|
||||
MediaStore.MediaColumns.BUCKET_ID,
|
||||
MediaStore.MediaColumns.DURATION,
|
||||
MediaStore.MediaColumns.DATE_TAKEN,
|
||||
MediaStore.MediaColumns.DATE_ADDED,
|
||||
MediaStore.MediaColumns.DATE_MODIFIED,
|
||||
)
|
||||
|
||||
val selectionParts = mutableListOf<String>()
|
||||
val selectionArgsList = mutableListOf<String>()
|
||||
|
||||
selectionParts.add(
|
||||
"(${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?)"
|
||||
)
|
||||
selectionArgsList.add(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString())
|
||||
selectionArgsList.add(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString())
|
||||
|
||||
if (storedGen != null) {
|
||||
selectionParts.add(
|
||||
"(${MediaStore.MediaColumns.GENERATION_MODIFIED} > ? OR ${MediaStore.MediaColumns.GENERATION_ADDED} > ?)"
|
||||
)
|
||||
selectionArgsList.add(storedGen.toString())
|
||||
selectionArgsList.add(storedGen.toString())
|
||||
}
|
||||
|
||||
val selection = selectionParts.joinToString(" AND ")
|
||||
val selectionArgs = selectionArgsList.toTypedArray()
|
||||
val sortOrder = null
|
||||
|
||||
ctx.contentResolver.query(
|
||||
queryUri,
|
||||
projection,
|
||||
selection,
|
||||
selectionArgs,
|
||||
sortOrder
|
||||
)?.use { cursor ->
|
||||
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)
|
||||
val mediaTypeColumn =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE)
|
||||
val nameColumn =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
|
||||
val dataColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
|
||||
val dateTakeColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_TAKEN)
|
||||
val dateAddedColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_ADDED)
|
||||
val dateModifiedColumn =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED)
|
||||
val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION)
|
||||
val bucketIdColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.BUCKET_ID)
|
||||
val formatter = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneOffset.UTC)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
val id = cursor.getLong(idColumn).toString()
|
||||
val path = cursor.getString(dataColumn)
|
||||
if (path.isBlank() || !File(path).exists()) {
|
||||
deleted.add(id)
|
||||
continue
|
||||
}
|
||||
|
||||
val mediaType = cursor.getInt(mediaTypeColumn)
|
||||
val name = cursor.getString(nameColumn)
|
||||
// Date taken is milliseconds since epoch
|
||||
var takenAt = cursor.getLong(dateTakeColumn)
|
||||
if (takenAt == 0L) {
|
||||
// Date added is seconds since epoch
|
||||
takenAt = cursor.getLong(dateAddedColumn) * 1000
|
||||
}
|
||||
val takenInstant = Instant.ofEpochMilli(takenAt)
|
||||
val createdAt = formatter.format(takenInstant)
|
||||
// Date modified is seconds since epoch
|
||||
val modifiedAt =
|
||||
formatter.format(Instant.ofEpochMilli(cursor.getLong(dateModifiedColumn) * 1000))
|
||||
// Duration is milliseconds
|
||||
val duration =
|
||||
if (mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) 0 else cursor.getLong(
|
||||
durationColumn
|
||||
) / 1000
|
||||
val bucketId = cursor.getString(bucketIdColumn)
|
||||
|
||||
changed.add(
|
||||
Asset(
|
||||
id,
|
||||
name,
|
||||
mediaType.toLong(),
|
||||
createdAt,
|
||||
modifiedAt,
|
||||
duration,
|
||||
listOf(bucketId)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unmounted volumes are handled in dart when the album is removed
|
||||
val syncDelta = SyncDelta(updates = changed, deletes = deleted)
|
||||
callback(Result.success(syncDelta))
|
||||
|
||||
} catch (e: Exception) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
}
|
@ -153,7 +153,7 @@ data class SyncDelta (
|
||||
|
||||
override fun hashCode(): Int = toList().hashCode()
|
||||
}
|
||||
private open class messagesPigeonCodec : StandardMessageCodec() {
|
||||
private open class MessagesPigeonCodec : StandardMessageCodec() {
|
||||
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||
return when (type) {
|
||||
129.toByte() -> {
|
||||
@ -191,11 +191,12 @@ interface ImHostService {
|
||||
fun hasMediaChanges(callback: (Result<Boolean>) -> Unit)
|
||||
fun getMediaChanges(callback: (Result<SyncDelta>) -> Unit)
|
||||
fun checkpointSync(callback: (Result<Unit>) -> Unit)
|
||||
fun getAssetIdsForAlbum(albumId: String, callback: (Result<List<String>>) -> Unit)
|
||||
|
||||
companion object {
|
||||
/** The codec used by ImHostService. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
messagesPigeonCodec()
|
||||
MessagesPigeonCodec()
|
||||
}
|
||||
/** Sets up an instance of `ImHostService` to handle messages through the `binaryMessenger`. */
|
||||
@JvmOverloads
|
||||
@ -273,6 +274,26 @@ interface ImHostService {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.ImHostService.getAssetIdsForAlbum$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val albumIdArg = args[0] as String
|
||||
api.getAssetIdsForAlbum(albumIdArg) { result: Result<List<String>> ->
|
||||
val error = result.exceptionOrNull()
|
||||
if (error != null) {
|
||||
reply.reply(MessagesPigeonUtils.wrapError(error))
|
||||
} else {
|
||||
val data = result.getOrNull()
|
||||
reply.reply(MessagesPigeonUtils.wrapResult(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package app.alextran.immich.platform
|
||||
|
||||
import ImHostService
|
||||
import SyncDelta
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.ext.SdkExtensions
|
||||
|
||||
class MessagesImpl(context: Context) : ImHostService {
|
||||
private val ctx: Context = context.applicationContext
|
||||
private val mediaManager: MediaManager = MediaManager(ctx)
|
||||
|
||||
override fun shouldFullSync(callback: (Result<Boolean>) -> Unit) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
mediaManager.shouldFullSync(callback)
|
||||
} else {
|
||||
callback(Result.success(true))
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasMediaChanges(callback: (Result<Boolean>) -> Unit) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1) {
|
||||
mediaManager.hasMediaChanges(callback)
|
||||
} else {
|
||||
callback(Result.failure(IllegalStateException("hasMediaChanges changes not supported on this Android version.")))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMediaChanges(callback: (Result<SyncDelta>) -> Unit) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1) {
|
||||
mediaManager.getMediaChanges(callback)
|
||||
} else {
|
||||
callback(Result.failure(IllegalStateException("getMediaChanges not supported on this Android version.")))
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkpointSync(callback: (Result<Unit>) -> Unit) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1) {
|
||||
mediaManager.checkpointSync(callback)
|
||||
} else {
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAssetIdsForAlbum(
|
||||
albumId: String,
|
||||
callback: (Result<List<String>>) -> Unit
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1) {
|
||||
mediaManager.getAssetIdsForAlbum(albumId, callback)
|
||||
} else {
|
||||
callback(Result.failure(IllegalStateException("getAssetIdsForAlbum not supported on this Android version.")))
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version '8.7.2' apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.22' apply false
|
||||
id 'com.google.devtools.ksp' version '2.0.20-1.0.24' apply false
|
||||
}
|
||||
|
||||
|
2
mobile/drift_schemas/main/drift_schema_v1.json
generated
2
mobile/drift_schemas/main/drift_schema_v1.json
generated
File diff suppressed because one or more lines are too long
@ -254,6 +254,7 @@ protocol ImHostService {
|
||||
func hasMediaChanges(completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
func getMediaChanges(completion: @escaping (Result<SyncDelta, Error>) -> Void)
|
||||
func checkpointSync(completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func getAssetIdsForAlbum(albumId: String, completion: @escaping (Result<[String], Error>) -> Void)
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
@ -329,5 +330,24 @@ class ImHostServiceSetup {
|
||||
} else {
|
||||
checkpointSyncChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getAssetIdsForAlbumChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ImHostService.getAssetIdsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ImHostService.getAssetIdsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
if let api = api {
|
||||
getAssetIdsForAlbumChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let albumIdArg = args[0] as! String
|
||||
api.getAssetIdsForAlbum(albumId: albumIdArg) { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getAssetIdsForAlbumChannel.setMessageHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,4 +41,8 @@ class ImHostServiceImpl: ImHostService {
|
||||
}
|
||||
}
|
||||
|
||||
func getAssetIdsForAlbum(albumId: String, completion: @escaping (Result<[String], any Error>) -> Void) {
|
||||
// Android specific, ignore the call with an empty list
|
||||
completion(.success([]))
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
|
||||
|
||||
Future<List<LocalAsset>> getAssetsForAlbum(String albumId);
|
||||
|
||||
Future<List<String>> getAssetIdsForAlbum(String albumId);
|
||||
|
||||
Future<void> update(LocalAlbum album);
|
||||
|
||||
Future<void> updateAll(Iterable<LocalAlbum> albums);
|
||||
@ -20,6 +22,8 @@ abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
|
||||
|
||||
Future<void> delete(String albumId);
|
||||
|
||||
Future<void> removeMissing(String albumId, Iterable<String> assetIds);
|
||||
|
||||
Future<void> removeAssets(String albumId, Iterable<String> assetIds);
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,12 @@ import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
import 'package:immich_mobile/platform/messages.g.dart' as platform;
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
|
||||
class DeviceSyncService {
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final ILocalAlbumRepository _localAlbumRepository;
|
||||
final Platform _platform;
|
||||
final platform.ImHostService _hostService;
|
||||
final Logger _log = Logger("SyncService");
|
||||
|
||||
@ -20,8 +22,10 @@ class DeviceSyncService {
|
||||
required IAlbumMediaRepository albumMediaRepository,
|
||||
required ILocalAlbumRepository localAlbumRepository,
|
||||
required platform.ImHostService hostService,
|
||||
Platform? platform,
|
||||
}) : _albumMediaRepository = albumMediaRepository,
|
||||
_localAlbumRepository = localAlbumRepository,
|
||||
_platform = platform ?? const LocalPlatform(),
|
||||
_hostService = hostService;
|
||||
|
||||
Future<void> sync() async {
|
||||
@ -41,6 +45,15 @@ class DeviceSyncService {
|
||||
|
||||
final delta = await _hostService.getMediaChanges();
|
||||
await _localAlbumRepository.handleSyncDelta(delta);
|
||||
|
||||
if (_platform.isAndroid) {
|
||||
final dbAlbums = await _localAlbumRepository.getAll();
|
||||
for (final album in dbAlbums) {
|
||||
final deviceIds = await _hostService.getAssetIdsForAlbum(album.id);
|
||||
await _localAlbumRepository.removeMissing(album.id, deviceIds);
|
||||
}
|
||||
}
|
||||
|
||||
await _hostService.checkpointSync();
|
||||
} catch (e, s) {
|
||||
_log.severe("Error performing device sync", e, s);
|
||||
@ -69,7 +82,7 @@ class DeviceSyncService {
|
||||
onlySecond: addAlbum,
|
||||
);
|
||||
|
||||
_hostService.checkpointSync();
|
||||
await _hostService.checkpointSync();
|
||||
stopwatch.stop();
|
||||
_log.info("Full device sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
} catch (e, s) {
|
||||
|
@ -97,8 +97,8 @@ class ExifInfo {
|
||||
class ExifEntity extends Table with DriftDefaultsMixin {
|
||||
const ExifEntity();
|
||||
|
||||
BlobColumn get assetId => blob()
|
||||
.references(RemoteAssetEntity, #remoteId, onDelete: KeyAction.cascade)();
|
||||
BlobColumn get assetId =>
|
||||
blob().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get city => text().nullable()();
|
||||
|
||||
|
@ -71,7 +71,7 @@ final class $$ExifEntityTableReferences extends i0.BaseReferences<
|
||||
.assetId,
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.remoteId));
|
||||
.id));
|
||||
|
||||
i4.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
||||
final $_column = $_itemColumn<i2.Uint8List>('asset_id')!;
|
||||
@ -81,7 +81,7 @@ final class $$ExifEntityTableReferences extends i0.BaseReferences<
|
||||
$_db,
|
||||
i5.ReadDatabaseContainer($_db)
|
||||
.resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity'))
|
||||
.filter((f) => f.remoteId.sqlEquals($_column));
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
@ -170,7 +170,7 @@ class $$ExifEntityTableFilterComposer
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.remoteId,
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
@ -270,7 +270,7 @@ class $$ExifEntityTableOrderingComposer
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.remoteId,
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
@ -364,7 +364,7 @@ class $$ExifEntityTableAnnotationComposer
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.remoteId,
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
@ -526,9 +526,8 @@ class $$ExifEntityTableTableManager extends i0.RootTableManager<
|
||||
currentColumn: table.assetId,
|
||||
referencedTable:
|
||||
i1.$$ExifEntityTableReferences._assetIdTable(db),
|
||||
referencedColumn: i1.$$ExifEntityTableReferences
|
||||
._assetIdTable(db)
|
||||
.remoteId,
|
||||
referencedColumn:
|
||||
i1.$$ExifEntityTableReferences._assetIdTable(db).id,
|
||||
) as T;
|
||||
}
|
||||
|
||||
@ -569,7 +568,7 @@ class $ExifEntityTable extends i3.ExifEntity
|
||||
type: i0.DriftSqlType.blob,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES remote_asset_entity (remote_id) ON DELETE CASCADE'));
|
||||
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE'));
|
||||
static const i0.VerificationMeta _cityMeta =
|
||||
const i0.VerificationMeta('city');
|
||||
@override
|
||||
|
@ -10,6 +10,8 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin {
|
||||
TextColumn get name => text()();
|
||||
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
||||
IntColumn get backupSelection => intEnum<BackupSelection>()();
|
||||
|
||||
// Used for mark & sweep
|
||||
BoolColumn get marker_ => boolean().withDefault(const Constant(false))();
|
||||
|
||||
@override
|
||||
|
@ -6,8 +6,8 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
class LocalAlbumAssetEntity extends Table with DriftDefaultsMixin {
|
||||
const LocalAlbumAssetEntity();
|
||||
|
||||
TextColumn get assetId => text()
|
||||
.references(LocalAssetEntity, #localId, onDelete: KeyAction.cascade)();
|
||||
TextColumn get assetId =>
|
||||
text().references(LocalAssetEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get albumId =>
|
||||
text().references(LocalAlbumEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
@ -39,7 +39,7 @@ final class $$LocalAlbumAssetEntityTableReferences extends i0.BaseReferences<
|
||||
.assetId,
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity')
|
||||
.localId));
|
||||
.id));
|
||||
|
||||
i3.$$LocalAssetEntityTableProcessedTableManager get assetId {
|
||||
final $_column = $_itemColumn<String>('asset_id')!;
|
||||
@ -49,7 +49,7 @@ final class $$LocalAlbumAssetEntityTableReferences extends i0.BaseReferences<
|
||||
$_db,
|
||||
i4.ReadDatabaseContainer($_db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'))
|
||||
.filter((f) => f.localId.sqlEquals($_column));
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
@ -99,7 +99,7 @@ class $$LocalAlbumAssetEntityTableFilterComposer
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
|
||||
getReferencedColumn: (t) => t.localId,
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
@ -154,7 +154,7 @@ class $$LocalAlbumAssetEntityTableOrderingComposer
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
|
||||
getReferencedColumn: (t) => t.localId,
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
@ -212,7 +212,7 @@ class $$LocalAlbumAssetEntityTableAnnotationComposer
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
|
||||
getReferencedColumn: (t) => t.localId,
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
@ -327,7 +327,7 @@ class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
._assetIdTable(db),
|
||||
referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences
|
||||
._assetIdTable(db)
|
||||
.localId,
|
||||
.id,
|
||||
) as T;
|
||||
}
|
||||
if (albumId) {
|
||||
@ -385,7 +385,7 @@ class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES local_asset_entity (local_id) ON DELETE CASCADE'));
|
||||
'REFERENCES local_asset_entity (id) ON DELETE CASCADE'));
|
||||
static const i0.VerificationMeta _albumIdMeta =
|
||||
const i0.VerificationMeta('albumId');
|
||||
@override
|
||||
|
@ -8,7 +8,7 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
||||
const LocalAssetEntity();
|
||||
|
||||
TextColumn get localId => text()();
|
||||
TextColumn get id => text()();
|
||||
|
||||
TextColumn get checksum => text().nullable()();
|
||||
|
||||
@ -16,13 +16,13 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
||||
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {localId};
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
extension LocalAssetEntityX on LocalAssetEntityData {
|
||||
LocalAsset toDto() {
|
||||
return LocalAsset(
|
||||
id: localId,
|
||||
id: id,
|
||||
name: name,
|
||||
checksum: checksum,
|
||||
type: type,
|
||||
|
@ -15,7 +15,7 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<int?> durationInSeconds,
|
||||
required String localId,
|
||||
required String id,
|
||||
i0.Value<String?> checksum,
|
||||
i0.Value<bool> isFavorite,
|
||||
});
|
||||
@ -26,7 +26,7 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<int?> durationInSeconds,
|
||||
i0.Value<String> localId,
|
||||
i0.Value<String> id,
|
||||
i0.Value<String?> checksum,
|
||||
i0.Value<bool> isFavorite,
|
||||
});
|
||||
@ -58,8 +58,8 @@ class $$LocalAssetEntityTableFilterComposer
|
||||
column: $table.durationInSeconds,
|
||||
builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get localId => $composableBuilder(
|
||||
column: $table.localId, builder: (column) => i0.ColumnFilters(column));
|
||||
i0.ColumnFilters<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get checksum => $composableBuilder(
|
||||
column: $table.checksum, builder: (column) => i0.ColumnFilters(column));
|
||||
@ -95,8 +95,8 @@ class $$LocalAssetEntityTableOrderingComposer
|
||||
column: $table.durationInSeconds,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get localId => $composableBuilder(
|
||||
column: $table.localId, builder: (column) => i0.ColumnOrderings(column));
|
||||
i0.ColumnOrderings<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get checksum => $composableBuilder(
|
||||
column: $table.checksum, builder: (column) => i0.ColumnOrderings(column));
|
||||
@ -130,8 +130,8 @@ class $$LocalAssetEntityTableAnnotationComposer
|
||||
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder(
|
||||
column: $table.durationInSeconds, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get localId =>
|
||||
$composableBuilder(column: $table.localId, builder: (column) => column);
|
||||
i0.GeneratedColumn<String> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get checksum =>
|
||||
$composableBuilder(column: $table.checksum, builder: (column) => column);
|
||||
@ -174,7 +174,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
i0.Value<String> localId = const i0.Value.absent(),
|
||||
i0.Value<String> id = const i0.Value.absent(),
|
||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||
}) =>
|
||||
@ -184,7 +184,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
localId: localId,
|
||||
id: id,
|
||||
checksum: checksum,
|
||||
isFavorite: isFavorite,
|
||||
),
|
||||
@ -194,7 +194,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
required String localId,
|
||||
required String id,
|
||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||
}) =>
|
||||
@ -204,7 +204,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
localId: localId,
|
||||
id: id,
|
||||
checksum: checksum,
|
||||
isFavorite: isFavorite,
|
||||
),
|
||||
@ -274,11 +274,10 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
||||
late final i0.GeneratedColumn<int> durationInSeconds =
|
||||
i0.GeneratedColumn<int>('duration_in_seconds', aliasedName, true,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: false);
|
||||
static const i0.VerificationMeta _localIdMeta =
|
||||
const i0.VerificationMeta('localId');
|
||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> localId = i0.GeneratedColumn<String>(
|
||||
'local_id', aliasedName, false,
|
||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||
'id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _checksumMeta =
|
||||
const i0.VerificationMeta('checksum');
|
||||
@ -303,7 +302,7 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
||||
createdAt,
|
||||
updatedAt,
|
||||
durationInSeconds,
|
||||
localId,
|
||||
id,
|
||||
checksum,
|
||||
isFavorite
|
||||
];
|
||||
@ -338,11 +337,10 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
||||
durationInSeconds.isAcceptableOrUnknown(
|
||||
data['duration_in_seconds']!, _durationInSecondsMeta));
|
||||
}
|
||||
if (data.containsKey('local_id')) {
|
||||
context.handle(_localIdMeta,
|
||||
localId.isAcceptableOrUnknown(data['local_id']!, _localIdMeta));
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_localIdMeta);
|
||||
context.missing(_idMeta);
|
||||
}
|
||||
if (data.containsKey('checksum')) {
|
||||
context.handle(_checksumMeta,
|
||||
@ -358,7 +356,7 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {localId};
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
i1.LocalAssetEntityData map(Map<String, dynamic> data,
|
||||
{String? tablePrefix}) {
|
||||
@ -375,8 +373,8 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
|
||||
durationInSeconds: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
|
||||
localId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}local_id'])!,
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||
checksum: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']),
|
||||
isFavorite: attachedDatabase.typeMapping
|
||||
@ -404,7 +402,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final int? durationInSeconds;
|
||||
final String localId;
|
||||
final String id;
|
||||
final String? checksum;
|
||||
final bool isFavorite;
|
||||
const LocalAssetEntityData(
|
||||
@ -413,7 +411,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.durationInSeconds,
|
||||
required this.localId,
|
||||
required this.id,
|
||||
this.checksum,
|
||||
required this.isFavorite});
|
||||
@override
|
||||
@ -429,7 +427,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
if (!nullToAbsent || durationInSeconds != null) {
|
||||
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
|
||||
}
|
||||
map['local_id'] = i0.Variable<String>(localId);
|
||||
map['id'] = i0.Variable<String>(id);
|
||||
if (!nullToAbsent || checksum != null) {
|
||||
map['checksum'] = i0.Variable<String>(checksum);
|
||||
}
|
||||
@ -447,7 +445,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
|
||||
localId: serializer.fromJson<String>(json['localId']),
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
checksum: serializer.fromJson<String?>(json['checksum']),
|
||||
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
||||
);
|
||||
@ -462,7 +460,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
||||
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
|
||||
'localId': serializer.toJson<String>(localId),
|
||||
'id': serializer.toJson<String>(id),
|
||||
'checksum': serializer.toJson<String?>(checksum),
|
||||
'isFavorite': serializer.toJson<bool>(isFavorite),
|
||||
};
|
||||
@ -474,7 +472,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
String? localId,
|
||||
String? id,
|
||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||
bool? isFavorite}) =>
|
||||
i1.LocalAssetEntityData(
|
||||
@ -485,7 +483,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
durationInSeconds: durationInSeconds.present
|
||||
? durationInSeconds.value
|
||||
: this.durationInSeconds,
|
||||
localId: localId ?? this.localId,
|
||||
id: id ?? this.id,
|
||||
checksum: checksum.present ? checksum.value : this.checksum,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
);
|
||||
@ -498,7 +496,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
durationInSeconds: data.durationInSeconds.present
|
||||
? data.durationInSeconds.value
|
||||
: this.durationInSeconds,
|
||||
localId: data.localId.present ? data.localId.value : this.localId,
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
checksum: data.checksum.present ? data.checksum.value : this.checksum,
|
||||
isFavorite:
|
||||
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
|
||||
@ -513,7 +511,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('durationInSeconds: $durationInSeconds, ')
|
||||
..write('localId: $localId, ')
|
||||
..write('id: $id, ')
|
||||
..write('checksum: $checksum, ')
|
||||
..write('isFavorite: $isFavorite')
|
||||
..write(')'))
|
||||
@ -522,7 +520,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(name, type, createdAt, updatedAt,
|
||||
durationInSeconds, localId, checksum, isFavorite);
|
||||
durationInSeconds, id, checksum, isFavorite);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
@ -532,7 +530,7 @@ class LocalAssetEntityData extends i0.DataClass
|
||||
other.createdAt == this.createdAt &&
|
||||
other.updatedAt == this.updatedAt &&
|
||||
other.durationInSeconds == this.durationInSeconds &&
|
||||
other.localId == this.localId &&
|
||||
other.id == this.id &&
|
||||
other.checksum == this.checksum &&
|
||||
other.isFavorite == this.isFavorite);
|
||||
}
|
||||
@ -544,7 +542,7 @@ class LocalAssetEntityCompanion
|
||||
final i0.Value<DateTime> createdAt;
|
||||
final i0.Value<DateTime> updatedAt;
|
||||
final i0.Value<int?> durationInSeconds;
|
||||
final i0.Value<String> localId;
|
||||
final i0.Value<String> id;
|
||||
final i0.Value<String?> checksum;
|
||||
final i0.Value<bool> isFavorite;
|
||||
const LocalAssetEntityCompanion({
|
||||
@ -553,7 +551,7 @@ class LocalAssetEntityCompanion
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.durationInSeconds = const i0.Value.absent(),
|
||||
this.localId = const i0.Value.absent(),
|
||||
this.id = const i0.Value.absent(),
|
||||
this.checksum = const i0.Value.absent(),
|
||||
this.isFavorite = const i0.Value.absent(),
|
||||
});
|
||||
@ -563,19 +561,19 @@ class LocalAssetEntityCompanion
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.durationInSeconds = const i0.Value.absent(),
|
||||
required String localId,
|
||||
required String id,
|
||||
this.checksum = const i0.Value.absent(),
|
||||
this.isFavorite = const i0.Value.absent(),
|
||||
}) : name = i0.Value(name),
|
||||
type = i0.Value(type),
|
||||
localId = i0.Value(localId);
|
||||
id = i0.Value(id);
|
||||
static i0.Insertable<i1.LocalAssetEntityData> custom({
|
||||
i0.Expression<String>? name,
|
||||
i0.Expression<int>? type,
|
||||
i0.Expression<DateTime>? createdAt,
|
||||
i0.Expression<DateTime>? updatedAt,
|
||||
i0.Expression<int>? durationInSeconds,
|
||||
i0.Expression<String>? localId,
|
||||
i0.Expression<String>? id,
|
||||
i0.Expression<String>? checksum,
|
||||
i0.Expression<bool>? isFavorite,
|
||||
}) {
|
||||
@ -585,7 +583,7 @@ class LocalAssetEntityCompanion
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (updatedAt != null) 'updated_at': updatedAt,
|
||||
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
|
||||
if (localId != null) 'local_id': localId,
|
||||
if (id != null) 'id': id,
|
||||
if (checksum != null) 'checksum': checksum,
|
||||
if (isFavorite != null) 'is_favorite': isFavorite,
|
||||
});
|
||||
@ -597,7 +595,7 @@ class LocalAssetEntityCompanion
|
||||
i0.Value<DateTime>? createdAt,
|
||||
i0.Value<DateTime>? updatedAt,
|
||||
i0.Value<int?>? durationInSeconds,
|
||||
i0.Value<String>? localId,
|
||||
i0.Value<String>? id,
|
||||
i0.Value<String?>? checksum,
|
||||
i0.Value<bool>? isFavorite}) {
|
||||
return i1.LocalAssetEntityCompanion(
|
||||
@ -606,7 +604,7 @@ class LocalAssetEntityCompanion
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
||||
localId: localId ?? this.localId,
|
||||
id: id ?? this.id,
|
||||
checksum: checksum ?? this.checksum,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
);
|
||||
@ -631,8 +629,8 @@ class LocalAssetEntityCompanion
|
||||
if (durationInSeconds.present) {
|
||||
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value);
|
||||
}
|
||||
if (localId.present) {
|
||||
map['local_id'] = i0.Variable<String>(localId.value);
|
||||
if (id.present) {
|
||||
map['id'] = i0.Variable<String>(id.value);
|
||||
}
|
||||
if (checksum.present) {
|
||||
map['checksum'] = i0.Variable<String>(checksum.value);
|
||||
@ -651,7 +649,7 @@ class LocalAssetEntityCompanion
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('durationInSeconds: $durationInSeconds, ')
|
||||
..write('localId: $localId, ')
|
||||
..write('id: $id, ')
|
||||
..write('checksum: $checksum, ')
|
||||
..write('isFavorite: $isFavorite')
|
||||
..write(')'))
|
||||
|
@ -8,7 +8,7 @@ class RemoteAssetEntity extends Table
|
||||
with DriftDefaultsMixin, AssetEntityMixin {
|
||||
const RemoteAssetEntity();
|
||||
|
||||
BlobColumn get remoteId => blob()();
|
||||
BlobColumn get id => blob()();
|
||||
|
||||
TextColumn get checksum => text().unique()();
|
||||
|
||||
@ -24,5 +24,5 @@ class RemoteAssetEntity extends Table
|
||||
DateTimeColumn get deletedAt => dateTime().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {remoteId};
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<int?> durationInSeconds,
|
||||
required i3.Uint8List remoteId,
|
||||
required i3.Uint8List id,
|
||||
required String checksum,
|
||||
i0.Value<bool> isFavorite,
|
||||
required i3.Uint8List ownerId,
|
||||
@ -34,7 +34,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<int?> durationInSeconds,
|
||||
i0.Value<i3.Uint8List> remoteId,
|
||||
i0.Value<i3.Uint8List> id,
|
||||
i0.Value<String> checksum,
|
||||
i0.Value<bool> isFavorite,
|
||||
i0.Value<i3.Uint8List> ownerId,
|
||||
@ -104,8 +104,8 @@ class $$RemoteAssetEntityTableFilterComposer
|
||||
column: $table.durationInSeconds,
|
||||
builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<i3.Uint8List> get remoteId => $composableBuilder(
|
||||
column: $table.remoteId, builder: (column) => i0.ColumnFilters(column));
|
||||
i0.ColumnFilters<i3.Uint8List> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get checksum => $composableBuilder(
|
||||
column: $table.checksum, builder: (column) => i0.ColumnFilters(column));
|
||||
@ -173,8 +173,8 @@ class $$RemoteAssetEntityTableOrderingComposer
|
||||
column: $table.durationInSeconds,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<i3.Uint8List> get remoteId => $composableBuilder(
|
||||
column: $table.remoteId, builder: (column) => i0.ColumnOrderings(column));
|
||||
i0.ColumnOrderings<i3.Uint8List> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get checksum => $composableBuilder(
|
||||
column: $table.checksum, builder: (column) => i0.ColumnOrderings(column));
|
||||
@ -242,8 +242,8 @@ class $$RemoteAssetEntityTableAnnotationComposer
|
||||
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder(
|
||||
column: $table.durationInSeconds, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<i3.Uint8List> get remoteId =>
|
||||
$composableBuilder(column: $table.remoteId, builder: (column) => column);
|
||||
i0.GeneratedColumn<i3.Uint8List> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get checksum =>
|
||||
$composableBuilder(column: $table.checksum, builder: (column) => column);
|
||||
@ -313,7 +313,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
i0.Value<i3.Uint8List> remoteId = const i0.Value.absent(),
|
||||
i0.Value<i3.Uint8List> id = const i0.Value.absent(),
|
||||
i0.Value<String> checksum = const i0.Value.absent(),
|
||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||
i0.Value<i3.Uint8List> ownerId = const i0.Value.absent(),
|
||||
@ -327,7 +327,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
remoteId: remoteId,
|
||||
id: id,
|
||||
checksum: checksum,
|
||||
isFavorite: isFavorite,
|
||||
ownerId: ownerId,
|
||||
@ -341,7 +341,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
required i3.Uint8List remoteId,
|
||||
required i3.Uint8List id,
|
||||
required String checksum,
|
||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||
required i3.Uint8List ownerId,
|
||||
@ -355,7 +355,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
remoteId: remoteId,
|
||||
id: id,
|
||||
checksum: checksum,
|
||||
isFavorite: isFavorite,
|
||||
ownerId: ownerId,
|
||||
@ -464,11 +464,10 @@ class $RemoteAssetEntityTable extends i4.RemoteAssetEntity
|
||||
late final i0.GeneratedColumn<int> durationInSeconds =
|
||||
i0.GeneratedColumn<int>('duration_in_seconds', aliasedName, true,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: false);
|
||||
static const i0.VerificationMeta _remoteIdMeta =
|
||||
const i0.VerificationMeta('remoteId');
|
||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||
@override
|
||||
late final i0.GeneratedColumn<i3.Uint8List> remoteId =
|
||||
i0.GeneratedColumn<i3.Uint8List>('remote_id', aliasedName, false,
|
||||
late final i0.GeneratedColumn<i3.Uint8List> id =
|
||||
i0.GeneratedColumn<i3.Uint8List>('id', aliasedName, false,
|
||||
type: i0.DriftSqlType.blob, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _checksumMeta =
|
||||
const i0.VerificationMeta('checksum');
|
||||
@ -522,7 +521,7 @@ class $RemoteAssetEntityTable extends i4.RemoteAssetEntity
|
||||
createdAt,
|
||||
updatedAt,
|
||||
durationInSeconds,
|
||||
remoteId,
|
||||
id,
|
||||
checksum,
|
||||
isFavorite,
|
||||
ownerId,
|
||||
@ -561,11 +560,10 @@ class $RemoteAssetEntityTable extends i4.RemoteAssetEntity
|
||||
durationInSeconds.isAcceptableOrUnknown(
|
||||
data['duration_in_seconds']!, _durationInSecondsMeta));
|
||||
}
|
||||
if (data.containsKey('remote_id')) {
|
||||
context.handle(_remoteIdMeta,
|
||||
remoteId.isAcceptableOrUnknown(data['remote_id']!, _remoteIdMeta));
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_remoteIdMeta);
|
||||
context.missing(_idMeta);
|
||||
}
|
||||
if (data.containsKey('checksum')) {
|
||||
context.handle(_checksumMeta,
|
||||
@ -603,7 +601,7 @@ class $RemoteAssetEntityTable extends i4.RemoteAssetEntity
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {remoteId};
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
i1.RemoteAssetEntityData map(Map<String, dynamic> data,
|
||||
{String? tablePrefix}) {
|
||||
@ -620,8 +618,8 @@ class $RemoteAssetEntityTable extends i4.RemoteAssetEntity
|
||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
|
||||
durationInSeconds: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
|
||||
remoteId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.blob, data['${effectivePrefix}remote_id'])!,
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.blob, data['${effectivePrefix}id'])!,
|
||||
checksum: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum'])!,
|
||||
isFavorite: attachedDatabase.typeMapping
|
||||
@ -657,7 +655,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final int? durationInSeconds;
|
||||
final i3.Uint8List remoteId;
|
||||
final i3.Uint8List id;
|
||||
final String checksum;
|
||||
final bool isFavorite;
|
||||
final i3.Uint8List ownerId;
|
||||
@ -670,7 +668,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.durationInSeconds,
|
||||
required this.remoteId,
|
||||
required this.id,
|
||||
required this.checksum,
|
||||
required this.isFavorite,
|
||||
required this.ownerId,
|
||||
@ -690,7 +688,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
if (!nullToAbsent || durationInSeconds != null) {
|
||||
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
|
||||
}
|
||||
map['remote_id'] = i0.Variable<i3.Uint8List>(remoteId);
|
||||
map['id'] = i0.Variable<i3.Uint8List>(id);
|
||||
map['checksum'] = i0.Variable<String>(checksum);
|
||||
map['is_favorite'] = i0.Variable<bool>(isFavorite);
|
||||
map['owner_id'] = i0.Variable<i3.Uint8List>(ownerId);
|
||||
@ -716,7 +714,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
|
||||
remoteId: serializer.fromJson<i3.Uint8List>(json['remoteId']),
|
||||
id: serializer.fromJson<i3.Uint8List>(json['id']),
|
||||
checksum: serializer.fromJson<String>(json['checksum']),
|
||||
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
||||
ownerId: serializer.fromJson<i3.Uint8List>(json['ownerId']),
|
||||
@ -735,7 +733,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
||||
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
|
||||
'remoteId': serializer.toJson<i3.Uint8List>(remoteId),
|
||||
'id': serializer.toJson<i3.Uint8List>(id),
|
||||
'checksum': serializer.toJson<String>(checksum),
|
||||
'isFavorite': serializer.toJson<bool>(isFavorite),
|
||||
'ownerId': serializer.toJson<i3.Uint8List>(ownerId),
|
||||
@ -751,7 +749,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
i3.Uint8List? remoteId,
|
||||
i3.Uint8List? id,
|
||||
String? checksum,
|
||||
bool? isFavorite,
|
||||
i3.Uint8List? ownerId,
|
||||
@ -766,7 +764,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
durationInSeconds: durationInSeconds.present
|
||||
? durationInSeconds.value
|
||||
: this.durationInSeconds,
|
||||
remoteId: remoteId ?? this.remoteId,
|
||||
id: id ?? this.id,
|
||||
checksum: checksum ?? this.checksum,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
@ -784,7 +782,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
durationInSeconds: data.durationInSeconds.present
|
||||
? data.durationInSeconds.value
|
||||
: this.durationInSeconds,
|
||||
remoteId: data.remoteId.present ? data.remoteId.value : this.remoteId,
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
checksum: data.checksum.present ? data.checksum.value : this.checksum,
|
||||
isFavorite:
|
||||
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
|
||||
@ -805,7 +803,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('durationInSeconds: $durationInSeconds, ')
|
||||
..write('remoteId: $remoteId, ')
|
||||
..write('id: $id, ')
|
||||
..write('checksum: $checksum, ')
|
||||
..write('isFavorite: $isFavorite, ')
|
||||
..write('ownerId: $ownerId, ')
|
||||
@ -823,7 +821,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
createdAt,
|
||||
updatedAt,
|
||||
durationInSeconds,
|
||||
i0.$driftBlobEquality.hash(remoteId),
|
||||
i0.$driftBlobEquality.hash(id),
|
||||
checksum,
|
||||
isFavorite,
|
||||
i0.$driftBlobEquality.hash(ownerId),
|
||||
@ -839,7 +837,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
other.createdAt == this.createdAt &&
|
||||
other.updatedAt == this.updatedAt &&
|
||||
other.durationInSeconds == this.durationInSeconds &&
|
||||
i0.$driftBlobEquality.equals(other.remoteId, this.remoteId) &&
|
||||
i0.$driftBlobEquality.equals(other.id, this.id) &&
|
||||
other.checksum == this.checksum &&
|
||||
other.isFavorite == this.isFavorite &&
|
||||
i0.$driftBlobEquality.equals(other.ownerId, this.ownerId) &&
|
||||
@ -855,7 +853,7 @@ class RemoteAssetEntityCompanion
|
||||
final i0.Value<DateTime> createdAt;
|
||||
final i0.Value<DateTime> updatedAt;
|
||||
final i0.Value<int?> durationInSeconds;
|
||||
final i0.Value<i3.Uint8List> remoteId;
|
||||
final i0.Value<i3.Uint8List> id;
|
||||
final i0.Value<String> checksum;
|
||||
final i0.Value<bool> isFavorite;
|
||||
final i0.Value<i3.Uint8List> ownerId;
|
||||
@ -868,7 +866,7 @@ class RemoteAssetEntityCompanion
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.durationInSeconds = const i0.Value.absent(),
|
||||
this.remoteId = const i0.Value.absent(),
|
||||
this.id = const i0.Value.absent(),
|
||||
this.checksum = const i0.Value.absent(),
|
||||
this.isFavorite = const i0.Value.absent(),
|
||||
this.ownerId = const i0.Value.absent(),
|
||||
@ -882,7 +880,7 @@ class RemoteAssetEntityCompanion
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.durationInSeconds = const i0.Value.absent(),
|
||||
required i3.Uint8List remoteId,
|
||||
required i3.Uint8List id,
|
||||
required String checksum,
|
||||
this.isFavorite = const i0.Value.absent(),
|
||||
required i3.Uint8List ownerId,
|
||||
@ -891,7 +889,7 @@ class RemoteAssetEntityCompanion
|
||||
this.deletedAt = const i0.Value.absent(),
|
||||
}) : name = i0.Value(name),
|
||||
type = i0.Value(type),
|
||||
remoteId = i0.Value(remoteId),
|
||||
id = i0.Value(id),
|
||||
checksum = i0.Value(checksum),
|
||||
ownerId = i0.Value(ownerId);
|
||||
static i0.Insertable<i1.RemoteAssetEntityData> custom({
|
||||
@ -900,7 +898,7 @@ class RemoteAssetEntityCompanion
|
||||
i0.Expression<DateTime>? createdAt,
|
||||
i0.Expression<DateTime>? updatedAt,
|
||||
i0.Expression<int>? durationInSeconds,
|
||||
i0.Expression<i3.Uint8List>? remoteId,
|
||||
i0.Expression<i3.Uint8List>? id,
|
||||
i0.Expression<String>? checksum,
|
||||
i0.Expression<bool>? isFavorite,
|
||||
i0.Expression<i3.Uint8List>? ownerId,
|
||||
@ -914,7 +912,7 @@ class RemoteAssetEntityCompanion
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (updatedAt != null) 'updated_at': updatedAt,
|
||||
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
|
||||
if (remoteId != null) 'remote_id': remoteId,
|
||||
if (id != null) 'id': id,
|
||||
if (checksum != null) 'checksum': checksum,
|
||||
if (isFavorite != null) 'is_favorite': isFavorite,
|
||||
if (ownerId != null) 'owner_id': ownerId,
|
||||
@ -930,7 +928,7 @@ class RemoteAssetEntityCompanion
|
||||
i0.Value<DateTime>? createdAt,
|
||||
i0.Value<DateTime>? updatedAt,
|
||||
i0.Value<int?>? durationInSeconds,
|
||||
i0.Value<i3.Uint8List>? remoteId,
|
||||
i0.Value<i3.Uint8List>? id,
|
||||
i0.Value<String>? checksum,
|
||||
i0.Value<bool>? isFavorite,
|
||||
i0.Value<i3.Uint8List>? ownerId,
|
||||
@ -943,7 +941,7 @@ class RemoteAssetEntityCompanion
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
||||
remoteId: remoteId ?? this.remoteId,
|
||||
id: id ?? this.id,
|
||||
checksum: checksum ?? this.checksum,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
@ -972,8 +970,8 @@ class RemoteAssetEntityCompanion
|
||||
if (durationInSeconds.present) {
|
||||
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value);
|
||||
}
|
||||
if (remoteId.present) {
|
||||
map['remote_id'] = i0.Variable<i3.Uint8List>(remoteId.value);
|
||||
if (id.present) {
|
||||
map['id'] = i0.Variable<i3.Uint8List>(id.value);
|
||||
}
|
||||
if (checksum.present) {
|
||||
map['checksum'] = i0.Variable<String>(checksum.value);
|
||||
@ -1004,7 +1002,7 @@ class RemoteAssetEntityCompanion
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('durationInSeconds: $durationInSeconds, ')
|
||||
..write('remoteId: $remoteId, ')
|
||||
..write('id: $id, ')
|
||||
..write('checksum: $checksum, ')
|
||||
..write('isFavorite: $isFavorite, ')
|
||||
..write('ownerId: $ownerId, ')
|
||||
|
@ -81,6 +81,31 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeMissing(String albumId, Iterable<String> assetIds) async {
|
||||
if (assetIds.isEmpty) {
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
final deleteSmt = _db.localAssetEntity.delete();
|
||||
deleteSmt.where((localAsset) {
|
||||
final subQuery = _db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.localAlbumEntity,
|
||||
_db.localAlbumAssetEntity.albumId
|
||||
.equalsExp(_db.localAlbumEntity.id),
|
||||
),
|
||||
]);
|
||||
subQuery.where(
|
||||
_db.localAlbumEntity.id.equals(albumId) &
|
||||
_db.localAlbumAssetEntity.assetId.isNotIn(assetIds),
|
||||
);
|
||||
return localAsset.id.isInQuery(subQuery);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeAssets(String albumId, Iterable<String> assetIds) async {
|
||||
if (assetIds.isEmpty) {
|
||||
@ -130,7 +155,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
return _db.transaction(() async {
|
||||
await _db.localAlbumEntity
|
||||
.update()
|
||||
.write(const LocalAlbumEntityCompanion(marker_: Value(false)));
|
||||
.write(const LocalAlbumEntityCompanion(marker_: Value(true)));
|
||||
|
||||
await _db.batch((batch) {
|
||||
for (final album in albums) {
|
||||
@ -150,6 +175,26 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
}
|
||||
});
|
||||
|
||||
if (_platform.isAndroid) {
|
||||
// On Android, an asset can only be in one album
|
||||
// So, get the albums that are marked for deletion
|
||||
// and delete all the assets that are in those albums
|
||||
final deleteSmt = _db.localAssetEntity.delete();
|
||||
deleteSmt.where((localAsset) {
|
||||
final subQuery = _db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.localAlbumEntity,
|
||||
_db.localAlbumAssetEntity.albumId
|
||||
.equalsExp(_db.localAlbumEntity.id),
|
||||
),
|
||||
]);
|
||||
subQuery.where(_db.localAlbumEntity.marker_.equals(false));
|
||||
return localAsset.id.isInQuery(subQuery);
|
||||
});
|
||||
}
|
||||
|
||||
await _db.localAlbumEntity.deleteWhere((f) => f.marker_.equals(false));
|
||||
});
|
||||
}
|
||||
@ -160,30 +205,41 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
[
|
||||
innerJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId
|
||||
.equalsExp(_db.localAssetEntity.localId),
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
),
|
||||
],
|
||||
)
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.desc(_db.localAssetEntity.localId)]);
|
||||
..orderBy([OrderingTerm.desc(_db.localAssetEntity.id)]);
|
||||
return query
|
||||
.map((row) => row.readTable(_db.localAssetEntity).toDto())
|
||||
.get();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getAssetIdsForAlbum(String albumId) {
|
||||
final query = _db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId));
|
||||
return query
|
||||
.map((row) => row.read(_db.localAlbumAssetEntity.assetId)!)
|
||||
.get();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> handleSyncDelta(platform.SyncDelta delta) {
|
||||
return _db.transaction(() async {
|
||||
await _deleteAssets(delta.deletes);
|
||||
|
||||
await _upsertAssets(delta.updates.map((a) => a.toLocalAsset()));
|
||||
await _db.batch((batch) {
|
||||
batch.deleteWhere(
|
||||
_db.localAlbumAssetEntity,
|
||||
(f) => f.assetId.isIn(delta.updates.map((a) => a.id)),
|
||||
);
|
||||
await _db.batch((batch) async {
|
||||
for (final asset in delta.updates) {
|
||||
batch.deleteWhere(
|
||||
_db.localAlbumAssetEntity,
|
||||
(f) =>
|
||||
f.albumId.isNotIn(asset.albumIds) & f.assetId.equals(asset.id),
|
||||
);
|
||||
|
||||
batch.insertAll(
|
||||
_db.localAlbumAssetEntity,
|
||||
asset.albumIds.map(
|
||||
@ -192,6 +248,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
albumId: albumId,
|
||||
),
|
||||
),
|
||||
onConflict: DoNothing(),
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -206,17 +263,14 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
return _db.batch(
|
||||
(batch) => batch.insertAll(
|
||||
_db.localAlbumAssetEntity,
|
||||
assets.map(
|
||||
(a) => LocalAlbumAssetEntityCompanion.insert(
|
||||
assetId: a.id,
|
||||
albumId: albumId,
|
||||
),
|
||||
return _db.localAlbumAssetEntity.insertAll(
|
||||
assets.map(
|
||||
(a) => LocalAlbumAssetEntityCompanion.insert(
|
||||
assetId: a.id,
|
||||
albumId: albumId,
|
||||
),
|
||||
mode: InsertMode.insertOrIgnore,
|
||||
),
|
||||
mode: InsertMode.insertOrIgnore,
|
||||
);
|
||||
}
|
||||
|
||||
@ -272,7 +326,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
createdAt: Value(a.createdAt),
|
||||
updatedAt: Value(a.updatedAt),
|
||||
durationInSeconds: Value.absentIfNull(a.durationInSeconds),
|
||||
localId: a.id,
|
||||
id: a.id,
|
||||
checksum: Value.absentIfNull(a.checksum),
|
||||
),
|
||||
),
|
||||
@ -288,7 +342,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
return _db.batch(
|
||||
(batch) => batch.deleteWhere(
|
||||
_db.localAssetEntity,
|
||||
(f) => f.localId.isIn(ids),
|
||||
(f) => f.id.isIn(ids),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository
|
||||
|
||||
@override
|
||||
Future<LocalAsset> get(String id) => _db.managers.localAssetEntity
|
||||
.filter((f) => f.localId(id))
|
||||
.filter((f) => f.id.equals(id))
|
||||
.map((a) => a.toDto())
|
||||
.getSingle();
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
||||
|
||||
batch.insert(
|
||||
_db.remoteAssetEntity,
|
||||
companion.copyWith(remoteId: Value(asset.id.toUuidByte())),
|
||||
companion.copyWith(id: Value(asset.id.toUuidByte())),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
}
|
||||
@ -191,9 +191,7 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
||||
for (final asset in assets) {
|
||||
batch.delete(
|
||||
_db.remoteAssetEntity,
|
||||
RemoteAssetEntityCompanion(
|
||||
remoteId: Value(asset.assetId.toUuidByte()),
|
||||
),
|
||||
RemoteAssetEntityCompanion(id: Value(asset.assetId.toUuidByte())),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ import 'package:pigeon/pigeon.dart';
|
||||
swiftOut: 'ios/Runner/Platform/Messages.g.swift',
|
||||
swiftOptions: SwiftOptions(),
|
||||
kotlinOut:
|
||||
'android/app/src/main/kotlin/app/alextran/immich/platform/messages.g.kt',
|
||||
'android/app/src/main/kotlin/app/alextran/immich/platform/Messages.g.kt',
|
||||
kotlinOptions: KotlinOptions(),
|
||||
dartOptions: DartOptions(),
|
||||
),
|
||||
@ -52,4 +52,8 @@ abstract class ImHostService {
|
||||
|
||||
@async
|
||||
void checkpointSync();
|
||||
|
||||
@async
|
||||
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
|
||||
List<String> getAssetIdsForAlbum(String albumId);
|
||||
}
|
||||
|
90
mobile/lib/platform/messages.g.dart
generated
90
mobile/lib/platform/messages.g.dart
generated
@ -14,22 +14,21 @@ PlatformException _createConnectionError(String channelName) {
|
||||
message: 'Unable to establish connection on channel: "$channelName".',
|
||||
);
|
||||
}
|
||||
|
||||
bool _deepEquals(Object? a, Object? b) {
|
||||
if (a is List && b is List) {
|
||||
return a.length == b.length &&
|
||||
a.indexed
|
||||
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
|
||||
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
|
||||
}
|
||||
if (a is Map && b is Map) {
|
||||
return a.length == b.length &&
|
||||
a.entries.every((MapEntry<Object?, Object?> entry) =>
|
||||
(b as Map<Object?, Object?>).containsKey(entry.key) &&
|
||||
_deepEquals(entry.value, b[entry.key]));
|
||||
return a.length == b.length && a.entries.every((MapEntry<Object?, Object?> entry) =>
|
||||
(b as Map<Object?, Object?>).containsKey(entry.key) &&
|
||||
_deepEquals(entry.value, b[entry.key]));
|
||||
}
|
||||
return a == b;
|
||||
}
|
||||
|
||||
|
||||
class Asset {
|
||||
Asset({
|
||||
required this.id,
|
||||
@ -68,8 +67,7 @@ class Asset {
|
||||
}
|
||||
|
||||
Object encode() {
|
||||
return _toList();
|
||||
}
|
||||
return _toList(); }
|
||||
|
||||
static Asset decode(Object result) {
|
||||
result as List<Object?>;
|
||||
@ -98,7 +96,8 @@ class Asset {
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
int get hashCode => Object.hashAll(_toList());
|
||||
int get hashCode => Object.hashAll(_toList())
|
||||
;
|
||||
}
|
||||
|
||||
class SyncDelta {
|
||||
@ -119,8 +118,7 @@ class SyncDelta {
|
||||
}
|
||||
|
||||
Object encode() {
|
||||
return _toList();
|
||||
}
|
||||
return _toList(); }
|
||||
|
||||
static SyncDelta decode(Object result) {
|
||||
result as List<Object?>;
|
||||
@ -144,9 +142,11 @@ class SyncDelta {
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
int get hashCode => Object.hashAll(_toList());
|
||||
int get hashCode => Object.hashAll(_toList())
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
class _PigeonCodec extends StandardMessageCodec {
|
||||
const _PigeonCodec();
|
||||
@override
|
||||
@ -154,10 +154,10 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
if (value is int) {
|
||||
buffer.putUint8(4);
|
||||
buffer.putInt64(value);
|
||||
} else if (value is Asset) {
|
||||
} else if (value is Asset) {
|
||||
buffer.putUint8(129);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is SyncDelta) {
|
||||
} else if (value is SyncDelta) {
|
||||
buffer.putUint8(130);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
@ -168,9 +168,9 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
@override
|
||||
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||
switch (type) {
|
||||
case 129:
|
||||
case 129:
|
||||
return Asset.decode(readValue(buffer)!);
|
||||
case 130:
|
||||
case 130:
|
||||
return SyncDelta.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
@ -182,11 +182,9 @@ class ImHostService {
|
||||
/// Constructor for [ImHostService]. The [binaryMessenger] named argument is
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
ImHostService(
|
||||
{BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
ImHostService({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||
pigeonVar_messageChannelSuffix =
|
||||
messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
final BinaryMessenger? pigeonVar_binaryMessenger;
|
||||
|
||||
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||
@ -194,10 +192,8 @@ class ImHostService {
|
||||
final String pigeonVar_messageChannelSuffix;
|
||||
|
||||
Future<bool> shouldFullSync() async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.ImHostService.shouldFullSync$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.shouldFullSync$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
@ -224,10 +220,8 @@ class ImHostService {
|
||||
}
|
||||
|
||||
Future<bool> hasMediaChanges() async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.ImHostService.hasMediaChanges$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.hasMediaChanges$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
@ -254,10 +248,8 @@ class ImHostService {
|
||||
}
|
||||
|
||||
Future<SyncDelta> getMediaChanges() async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.ImHostService.getMediaChanges$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.getMediaChanges$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
@ -284,10 +276,8 @@ class ImHostService {
|
||||
}
|
||||
|
||||
Future<void> checkpointSync() async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.ImHostService.checkpointSync$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.checkpointSync$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
@ -307,4 +297,32 @@ class ImHostService {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<String>> getAssetIdsForAlbum(String albumId) async {
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.getAssetIdsForAlbum$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[albumId]);
|
||||
final List<Object?>? pigeonVar_replyList =
|
||||
await pigeonVar_sendFuture as List<Object?>?;
|
||||
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<Object?>?)!.cast<String>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
.PHONY: build watch create_app_icon create_splash build_release_android
|
||||
|
||||
build:
|
||||
dart run pigeon --input lib/platform/messages.dart
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
|
||||
watch:
|
||||
|
Loading…
x
Reference in New Issue
Block a user