mirror of
https://github.com/immich-app/immich.git
synced 2026-06-04 05:05:22 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e5f89bc87 | |||
| 911dde39c9 | |||
| afa836181c | |||
| 963862b1b9 | |||
| 96d521e149 |
@@ -4,6 +4,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'open-api/**'
|
||||
- 'mobile/lib/utils/openapi_patching.dart'
|
||||
- '.github/workflows/check-openapi.yml'
|
||||
|
||||
concurrency:
|
||||
@@ -29,3 +30,35 @@ jobs:
|
||||
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
||||
revision: open-api/immich-openapi-specs.json
|
||||
fail-on: ERR
|
||||
|
||||
check-mobile-patches:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Mise
|
||||
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||
with:
|
||||
github_token: ${{ github.token }}
|
||||
|
||||
- name: Get packages
|
||||
working-directory: ./mobile
|
||||
run: flutter pub get
|
||||
|
||||
- name: Fetch base spec from main
|
||||
run: |
|
||||
curl -fsSL \
|
||||
"https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json" \
|
||||
-o /tmp/base-spec.json
|
||||
|
||||
- name: Check newly-required fields have a backward-compat patch
|
||||
working-directory: ./mobile
|
||||
env:
|
||||
OPENAPI_BASE_SPEC: /tmp/base-spec.json
|
||||
OPENAPI_REVISION_SPEC: ../open-api/immich-openapi-specs.json
|
||||
run: flutter test test/openapi_patches_coverage.dart
|
||||
|
||||
+66
-34
@@ -542,16 +542,17 @@ private open class MessagesPigeonCodec : StandardMessageCodec() {
|
||||
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface NativeSyncApi {
|
||||
fun shouldFullSync(): Boolean
|
||||
fun getMediaChanges(): SyncDelta
|
||||
fun shouldFullSync(callback: (Result<Boolean>) -> Unit)
|
||||
fun getMediaChanges(callback: (Result<SyncDelta>) -> Unit)
|
||||
fun checkpointSync()
|
||||
fun clearSyncCheckpoint()
|
||||
fun getAssetIdsForAlbum(albumId: String): List<String>
|
||||
fun getAlbums(): List<PlatformAlbum>
|
||||
fun getAssetIdsForAlbum(albumId: String, callback: (Result<List<String>>) -> Unit)
|
||||
fun getAlbums(callback: (Result<List<PlatformAlbum>>) -> Unit)
|
||||
fun getAssetsCountSince(albumId: String, timestamp: Long): Long
|
||||
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset>
|
||||
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?, callback: (Result<List<PlatformAsset>>) -> Unit)
|
||||
fun hashAssets(assetIds: List<String>, allowNetworkAccess: Boolean, callback: (Result<List<HashResult>>) -> Unit)
|
||||
fun cancelHashing()
|
||||
fun cancelSync()
|
||||
fun getTrashedAssets(): Map<String, List<PlatformAsset>>
|
||||
fun restoreFromTrashById(mediaId: String, type: Long, callback: (Result<Boolean>) -> Unit)
|
||||
fun getCloudIdForAssetIds(assetIds: List<String>): List<CloudIdResult>
|
||||
@@ -570,27 +571,33 @@ interface NativeSyncApi {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.shouldFullSync$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.shouldFullSync())
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
api.shouldFullSync{ result: Result<Boolean> ->
|
||||
val error = result.exceptionOrNull()
|
||||
if (error != null) {
|
||||
reply.reply(MessagesPigeonUtils.wrapError(error))
|
||||
} else {
|
||||
val data = result.getOrNull()
|
||||
reply.reply(MessagesPigeonUtils.wrapResult(data))
|
||||
}
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getMediaChanges())
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
api.getMediaChanges{ result: Result<SyncDelta> ->
|
||||
val error = result.exceptionOrNull()
|
||||
if (error != null) {
|
||||
reply.reply(MessagesPigeonUtils.wrapError(error))
|
||||
} else {
|
||||
val data = result.getOrNull()
|
||||
reply.reply(MessagesPigeonUtils.wrapResult(data))
|
||||
}
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
@@ -629,32 +636,38 @@ interface NativeSyncApi {
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val albumIdArg = args[0] as String
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getAssetIdsForAlbum(albumIdArg))
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
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))
|
||||
}
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getAlbums())
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
api.getAlbums{ result: Result<List<PlatformAlbum>> ->
|
||||
val error = result.exceptionOrNull()
|
||||
if (error != null) {
|
||||
reply.reply(MessagesPigeonUtils.wrapError(error))
|
||||
} else {
|
||||
val data = result.getOrNull()
|
||||
reply.reply(MessagesPigeonUtils.wrapResult(data))
|
||||
}
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
@@ -679,18 +692,21 @@ interface NativeSyncApi {
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val albumIdArg = args[0] as String
|
||||
val updatedTimeCondArg = args[1] as Long?
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getAssetsForAlbum(albumIdArg, updatedTimeCondArg))
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
api.getAssetsForAlbum(albumIdArg, updatedTimeCondArg) { result: Result<List<PlatformAsset>> ->
|
||||
val error = result.exceptionOrNull()
|
||||
if (error != null) {
|
||||
reply.reply(MessagesPigeonUtils.wrapError(error))
|
||||
} else {
|
||||
val data = result.getOrNull()
|
||||
reply.reply(MessagesPigeonUtils.wrapResult(data))
|
||||
}
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
@@ -733,6 +749,22 @@ interface NativeSyncApi {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.cancelSync$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
api.cancelSync()
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssets$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
if (api != null) {
|
||||
|
||||
@@ -4,7 +4,11 @@ import android.content.Context
|
||||
|
||||
|
||||
class NativeSyncApiImpl26(context: Context) : NativeSyncApiImplBase(context), NativeSyncApi {
|
||||
override fun shouldFullSync(): Boolean {
|
||||
override fun shouldFullSync(callback: (Result<Boolean>) -> Unit) {
|
||||
runSync(callback) { shouldFullSync() }
|
||||
}
|
||||
|
||||
private fun shouldFullSync(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -18,7 +22,11 @@ class NativeSyncApiImpl26(context: Context) : NativeSyncApiImplBase(context), Na
|
||||
// No-op for Android 10 and below
|
||||
}
|
||||
|
||||
override fun getMediaChanges(): SyncDelta {
|
||||
override fun getMediaChanges(callback: (Result<SyncDelta>) -> Unit) {
|
||||
runSync(callback) { getMediaChanges() }
|
||||
}
|
||||
|
||||
private fun getMediaChanges(): SyncDelta {
|
||||
throw IllegalStateException("Method not supported on this Android version.")
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.RequiresExtension
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
@@ -35,7 +37,11 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldFullSync(): Boolean =
|
||||
override fun shouldFullSync(callback: (Result<Boolean>) -> Unit) {
|
||||
runSync(callback) { shouldFullSync() }
|
||||
}
|
||||
|
||||
private fun shouldFullSync(): Boolean =
|
||||
MediaStore.getVersion(ctx) != prefs.getString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, null)
|
||||
|
||||
override fun checkpointSync() {
|
||||
@@ -49,7 +55,11 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMediaChanges(): SyncDelta {
|
||||
override fun getMediaChanges(callback: (Result<SyncDelta>) -> Unit) {
|
||||
runSync(callback) { getMediaChanges() }
|
||||
}
|
||||
|
||||
private suspend fun getMediaChanges(): SyncDelta {
|
||||
val genMap = getSavedGenerationMap()
|
||||
val currentVolumes = MediaStore.getExternalVolumeNames(ctx)
|
||||
val changed = mutableListOf<PlatformAsset>()
|
||||
@@ -58,6 +68,7 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na
|
||||
var hasChanges = genMap.keys != currentVolumes
|
||||
|
||||
for (volume in currentVolumes) {
|
||||
currentCoroutineContext().ensureActive()
|
||||
val currentGen = MediaStore.getGeneration(ctx, volume)
|
||||
val storedGen = genMap[volume] ?: 0
|
||||
if (currentGen <= storedGen) {
|
||||
|
||||
@@ -45,12 +45,14 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin(), ActivityAwa
|
||||
private val ctx: Context = context.applicationContext
|
||||
|
||||
private var hashTask: Job? = null
|
||||
private var syncJob: Job? = null
|
||||
private val mediaTrashDelegate = MediaTrashDelegate(ctx)
|
||||
|
||||
companion object {
|
||||
private const val MAX_CONCURRENT_HASH_OPERATIONS = 16
|
||||
private val hashSemaphore = Semaphore(MAX_CONCURRENT_HASH_OPERATIONS)
|
||||
private const val HASHING_CANCELLED_CODE = "HASH_CANCELLED"
|
||||
private const val SYNC_CANCELLED_CODE = "SYNC_CANCELLED"
|
||||
|
||||
// MediaStore.Files.FileColumns.SPECIAL_FORMAT — S Extensions 21+
|
||||
// https://developer.android.com/reference/android/provider/MediaStore.Files.FileColumns#SPECIAL_FORMAT
|
||||
@@ -295,7 +297,11 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin(), ActivityAwa
|
||||
return PlatformAssetPlaybackStyle.IMAGE
|
||||
}
|
||||
|
||||
fun getAlbums(): List<PlatformAlbum> {
|
||||
fun getAlbums(callback: (Result<List<PlatformAlbum>>) -> Unit) {
|
||||
runSync(callback) { getAlbums() }
|
||||
}
|
||||
|
||||
private suspend fun getAlbums(): List<PlatformAlbum> {
|
||||
val albums = mutableListOf<PlatformAlbum>()
|
||||
val albumsCount = mutableMapOf<String, Int>()
|
||||
|
||||
@@ -322,6 +328,7 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin(), ActivityAwa
|
||||
cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATE_MODIFIED)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
currentCoroutineContext().ensureActive()
|
||||
val id = cursor.getString(bucketIdColumn)
|
||||
|
||||
val count = albumsCount.getOrDefault(id, 0)
|
||||
@@ -342,7 +349,11 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin(), ActivityAwa
|
||||
.sortedBy { it.id }
|
||||
}
|
||||
|
||||
fun getAssetIdsForAlbum(albumId: String): List<String> {
|
||||
fun getAssetIdsForAlbum(albumId: String, callback: (Result<List<String>>) -> Unit) {
|
||||
runSync(callback) { getAssetIdsForAlbum(albumId) }
|
||||
}
|
||||
|
||||
private fun getAssetIdsForAlbum(albumId: String): List<String> {
|
||||
val projection = arrayOf(MediaStore.MediaColumns._ID)
|
||||
|
||||
return getCursor(
|
||||
@@ -366,7 +377,11 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin(), ActivityAwa
|
||||
)?.use { cursor -> cursor.count.toLong() } ?: 0L
|
||||
|
||||
|
||||
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset> {
|
||||
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?, callback: (Result<List<PlatformAsset>>) -> Unit) {
|
||||
runSync(callback) { getAssetsForAlbum(albumId, updatedTimeCond) }
|
||||
}
|
||||
|
||||
private fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset> {
|
||||
var selection = "$BUCKET_SELECTION AND $MEDIA_SELECTION"
|
||||
val selectionArgs = mutableListOf(albumId, *MEDIA_SELECTION_ARGS)
|
||||
|
||||
@@ -451,6 +466,24 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin(), ActivityAwa
|
||||
hashTask = null
|
||||
}
|
||||
|
||||
fun cancelSync() {
|
||||
syncJob?.cancel()
|
||||
syncJob = null
|
||||
}
|
||||
|
||||
protected fun <T> runSync(callback: (Result<T>) -> Unit, work: suspend () -> T) {
|
||||
syncJob?.cancel()
|
||||
syncJob = CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
completeWhenActive(callback, Result.success(work()))
|
||||
} catch (e: CancellationException) {
|
||||
completeWhenActive(callback, Result.failure(FlutterError(SYNC_CANCELLED_CODE, "Sync cancelled", null)))
|
||||
} catch (e: Exception) {
|
||||
completeWhenActive(callback, Result.failure(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreFromTrashById(mediaId: String, type: Long, callback: (Result<Boolean>) -> Unit) {
|
||||
mediaTrashDelegate.restoreFromTrashById(mediaId, type) { completeWhenActive(callback, it) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/main.dart' as app;
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/bootstrap.dart';
|
||||
import 'package:immich_mobile/wm_executor.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
import 'test_utils/fake_immich_server.dart';
|
||||
|
||||
void main() {
|
||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
// These tests do real I/O without pumping a widget tree, so disable the fake async clock
|
||||
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
||||
|
||||
late Drift drift;
|
||||
late FakeImmichServer server;
|
||||
|
||||
setUpAll(() async {
|
||||
await app.initApp();
|
||||
(drift, _) = await Bootstrap.initDomain();
|
||||
});
|
||||
|
||||
setUp(() async {
|
||||
await workerManagerPatch.init(dynamicSpawning: true);
|
||||
server = await FakeImmichServer.start();
|
||||
await ApiService().resolveAndSetEndpoint(server.endpoint);
|
||||
await drift.delete(drift.userEntity).go();
|
||||
await Store.delete(StoreKey.syncMigrationStatus);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await workerManagerPatch.dispose();
|
||||
await server.close();
|
||||
await Store.delete(StoreKey.serverEndpoint);
|
||||
await Store.delete(StoreKey.syncMigrationStatus);
|
||||
});
|
||||
|
||||
void sendUser(SyncStream stream, String id, String name) {
|
||||
stream.send(
|
||||
type: SyncEntityType.userV1.value,
|
||||
data: SyncUserV1(
|
||||
id: id,
|
||||
name: name,
|
||||
email: '$id@test.com',
|
||||
hasProfileImage: false,
|
||||
deletedAt: null,
|
||||
profileChangedAt: DateTime.utc(2025),
|
||||
).toJson(),
|
||||
ack: id,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> dbReadable() async {
|
||||
try {
|
||||
await drift.customSelect('SELECT 1').get().timeout(const Duration(seconds: 5));
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> userCount() async => (await drift.select(drift.userEntity).get()).length;
|
||||
|
||||
// Starts a remote sync and resolves once its /sync/stream request is open.
|
||||
Future<(Future<bool>, SyncStream)> startSync() async {
|
||||
final sync = BackgroundSyncManager().syncRemote();
|
||||
final stream = await server.streamOpened.timeout(
|
||||
const Duration(seconds: 30),
|
||||
onTimeout: () => fail('sync isolate never opened /sync/stream'),
|
||||
);
|
||||
return (sync, stream);
|
||||
}
|
||||
|
||||
testWidgets('a full sync ingests streamed events into the shared DB', (tester) async {
|
||||
expect(await userCount(), 0);
|
||||
|
||||
final (sync, stream) = await startSync();
|
||||
|
||||
sendUser(stream, 'u1', 'Alice');
|
||||
sendUser(stream, 'u2', 'Bob');
|
||||
await stream.close();
|
||||
|
||||
final result = await sync.timeout(
|
||||
const Duration(seconds: 30),
|
||||
onTimeout: () => fail('sync did not complete after the stream ended'),
|
||||
);
|
||||
expect(result, isTrue);
|
||||
expect(await userCount(), 2);
|
||||
expect(server.ackRequests, greaterThan(0));
|
||||
});
|
||||
|
||||
testWidgets('disposing the pool during an in-flight sync drains promptly', (tester) async {
|
||||
final (sync, _) = await startSync();
|
||||
|
||||
final sw = Stopwatch()..start();
|
||||
await workerManagerPatch.dispose().timeout(
|
||||
const Duration(seconds: 15),
|
||||
onTimeout: () => fail('dispose() hung — worker did not drain and exit'),
|
||||
);
|
||||
expect(sw.elapsed, lessThan(const Duration(seconds: 10)), reason: 'abort-driven, not socket-timeout bound');
|
||||
|
||||
expect(await sync.timeout(const Duration(seconds: 5), onTimeout: () => false), isFalse);
|
||||
});
|
||||
|
||||
testWidgets('tearing down a worker blocked mid-write leaves the DB usable', (tester) async {
|
||||
final (sync, stream) = await startSync();
|
||||
|
||||
// Hold an exclusive write transaction so the worker's write is blocked. The lock is taken only
|
||||
// after the stream opens to avoid blocking the worker's own startup DB reads.
|
||||
final releaseTxn = Completer<void>();
|
||||
final txnHeld = Completer<void>();
|
||||
final txn = drift.transaction(() async {
|
||||
await drift.into(drift.userEntity).insert(
|
||||
UserEntityCompanion.insert(
|
||||
id: 'holder',
|
||||
name: 'holder',
|
||||
email: 'holder@test.com',
|
||||
hasProfileImage: const Value(false),
|
||||
profileChangedAt: Value(DateTime.utc(2025)),
|
||||
),
|
||||
);
|
||||
txnHeld.complete();
|
||||
await releaseTxn.future;
|
||||
});
|
||||
await txnHeld.future;
|
||||
|
||||
sendUser(stream, 'u1', 'Alice');
|
||||
await stream.close();
|
||||
|
||||
// dispose() can only finish once the worker unwinds, which is blocked on the
|
||||
// lock — so start it, release the lock, then await completion.
|
||||
final disposed = workerManagerPatch.dispose();
|
||||
releaseTxn.complete();
|
||||
await txn;
|
||||
await disposed.timeout(
|
||||
const Duration(seconds: 15),
|
||||
onTimeout: () => fail('dispose() hung after releasing the write lock'),
|
||||
);
|
||||
await sync.timeout(const Duration(seconds: 5), onTimeout: () => false);
|
||||
|
||||
expect(await dbReadable(), isTrue);
|
||||
final users = await drift.select(drift.userEntity).get();
|
||||
expect(users.map((u) => u.id), contains('holder'));
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
/// A dummy localhost server that implements only the endpoints that remote-sync touches.
|
||||
class FakeImmichServer {
|
||||
FakeImmichServer._(this._server, this.version);
|
||||
|
||||
final HttpServer _server;
|
||||
final (int, int, int) version;
|
||||
|
||||
final Completer<SyncStream> _streamOpened = Completer<SyncStream>();
|
||||
|
||||
int ackRequests = 0;
|
||||
|
||||
String get endpoint => 'http://${_server.address.host}:${_server.port}/api';
|
||||
|
||||
/// Resolves when the sync isolate opens `POST /sync/stream`.
|
||||
Future<SyncStream> get streamOpened => _streamOpened.future;
|
||||
|
||||
static Future<FakeImmichServer> start({(int, int, int) version = (3, 0, 0)}) async {
|
||||
final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
|
||||
final fake = FakeImmichServer._(server, version);
|
||||
fake._listen();
|
||||
return fake;
|
||||
}
|
||||
|
||||
void _listen() {
|
||||
// A connection torn down mid-write during teardown is expected
|
||||
_server.listen((request) => unawaited(_route(request).catchError((_) {})));
|
||||
}
|
||||
|
||||
Future<void> _route(HttpRequest request) async {
|
||||
final method = request.method;
|
||||
final path = request.uri.path;
|
||||
|
||||
if (method == 'GET' && path == '/api/server/ping') {
|
||||
return _respondJson(request, {'res': 'pong'});
|
||||
}
|
||||
if (method == 'GET' && path == '/api/server/version') {
|
||||
final (major, minor, patch) = version;
|
||||
return _respondJson(request, {'major': major, 'minor': minor, 'patch': patch});
|
||||
}
|
||||
if (path == '/api/sync/ack') {
|
||||
if (method != 'DELETE') {
|
||||
ackRequests++;
|
||||
}
|
||||
return _respondEmpty(request);
|
||||
}
|
||||
if (method == 'POST' && path == '/api/sync/stream') {
|
||||
return _openSyncStream(request);
|
||||
}
|
||||
return _respondEmpty(request, status: HttpStatus.notFound);
|
||||
}
|
||||
|
||||
Future<void> _openSyncStream(HttpRequest request) async {
|
||||
await request.drain<void>();
|
||||
request.response
|
||||
..statusCode = HttpStatus.ok
|
||||
..headers.contentType = ContentType('application', 'jsonlines+json')
|
||||
..contentLength = -1 // chunked: stays open to stream incrementally
|
||||
..bufferOutput = false;
|
||||
// Flush headers so the client's send() resolves and enters its read loop.
|
||||
await request.response.flush();
|
||||
if (!_streamOpened.isCompleted) {
|
||||
_streamOpened.complete(SyncStream._(request.response));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _respondJson(HttpRequest request, Object body) async {
|
||||
await request.drain<void>();
|
||||
request.response
|
||||
..statusCode = HttpStatus.ok
|
||||
..headers.contentType = ContentType.json
|
||||
..write(jsonEncode(body));
|
||||
await request.response.close();
|
||||
}
|
||||
|
||||
Future<void> _respondEmpty(HttpRequest request, {int status = HttpStatus.ok}) async {
|
||||
await request.drain<void>();
|
||||
request.response.statusCode = status;
|
||||
await request.response.close();
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
if (_streamOpened.isCompleted) {
|
||||
await (await _streamOpened.future).close();
|
||||
}
|
||||
await _server.close(force: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to the open `/sync/stream` response: push jsonlines events, then end.
|
||||
class SyncStream {
|
||||
SyncStream._(this._response);
|
||||
|
||||
final HttpResponse _response;
|
||||
bool _closed = false;
|
||||
|
||||
/// [data] should be a Sync*V1 DTO's `toJson()` so the parser's `fromJson` round-trips it.
|
||||
void send({required String type, required Object data, required String ack}) {
|
||||
if (_closed) {
|
||||
return;
|
||||
}
|
||||
_response.write('${jsonEncode({'type': type, 'data': data, 'ack': ack})}\n');
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
if (_closed) {
|
||||
return;
|
||||
}
|
||||
_closed = true;
|
||||
await _response.close();
|
||||
}
|
||||
}
|
||||
@@ -121,8 +121,8 @@ class BackgroundWorker: BackgroundWorkerBgHostApi {
|
||||
|
||||
/**
|
||||
* Cancels the currently running background task, either due to timeout or external request.
|
||||
* Sends a cancel signal to the Flutter side and sets up a fallback timer to ensure
|
||||
* the completion handler is eventually called even if Flutter doesn't respond.
|
||||
* Only tears down the engine after Dart confirms it's drained. If Dart overruns iOS's grace window,
|
||||
* the expiration handler still calls setTaskCompleted and iOS suspends us.
|
||||
*/
|
||||
func close() {
|
||||
if isComplete {
|
||||
@@ -132,12 +132,6 @@ class BackgroundWorker: BackgroundWorkerBgHostApi {
|
||||
flutterApi?.cancel { result in
|
||||
self.complete(success: false)
|
||||
}
|
||||
|
||||
// Fallback safety mechanism: ensure completion is called within 2 seconds
|
||||
// This prevents the background task from hanging indefinitely if Flutter doesn't respond
|
||||
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in
|
||||
self.complete(success: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Generated
+58
-42
@@ -526,16 +526,17 @@ class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
|
||||
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol NativeSyncApi {
|
||||
func shouldFullSync() throws -> Bool
|
||||
func getMediaChanges() throws -> SyncDelta
|
||||
func shouldFullSync(completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
func getMediaChanges(completion: @escaping (Result<SyncDelta, Error>) -> Void)
|
||||
func checkpointSync() throws
|
||||
func clearSyncCheckpoint() throws
|
||||
func getAssetIdsForAlbum(albumId: String) throws -> [String]
|
||||
func getAlbums() throws -> [PlatformAlbum]
|
||||
func getAssetIdsForAlbum(albumId: String, completion: @escaping (Result<[String], Error>) -> Void)
|
||||
func getAlbums(completion: @escaping (Result<[PlatformAlbum], Error>) -> Void)
|
||||
func getAssetsCountSince(albumId: String, timestamp: Int64) throws -> Int64
|
||||
func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset]
|
||||
func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?, completion: @escaping (Result<[PlatformAsset], Error>) -> Void)
|
||||
func hashAssets(assetIds: [String], allowNetworkAccess: Bool, completion: @escaping (Result<[HashResult], Error>) -> Void)
|
||||
func cancelHashing() throws
|
||||
func cancelSync() throws
|
||||
func getTrashedAssets() throws -> [String: [PlatformAsset]]
|
||||
func restoreFromTrashById(mediaId: String, type: Int64, completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
func getCloudIdForAssetIds(assetIds: [String]) throws -> [CloudIdResult]
|
||||
@@ -555,26 +556,28 @@ class NativeSyncApiSetup {
|
||||
let shouldFullSyncChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.shouldFullSync\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
shouldFullSyncChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
let result = try api.shouldFullSync()
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
api.shouldFullSync { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shouldFullSyncChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getMediaChangesChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
let getMediaChangesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
getMediaChangesChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
let result = try api.getMediaChanges()
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
api.getMediaChanges { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -606,33 +609,33 @@ class NativeSyncApiSetup {
|
||||
} else {
|
||||
clearSyncCheckpointChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getAssetIdsForAlbumChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
let getAssetIdsForAlbumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
getAssetIdsForAlbumChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let albumIdArg = args[0] as! String
|
||||
do {
|
||||
let result = try api.getAssetIdsForAlbum(albumId: albumIdArg)
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
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)
|
||||
}
|
||||
let getAlbumsChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
let getAlbumsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
getAlbumsChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
let result = try api.getAlbums()
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
api.getAlbums { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -656,19 +659,19 @@ class NativeSyncApiSetup {
|
||||
} else {
|
||||
getAssetsCountSinceChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getAssetsForAlbumChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
let getAssetsForAlbumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
getAssetsForAlbumChannel.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.getAssetsForAlbum(albumId: albumIdArg, updatedTimeCond: updatedTimeCondArg)
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
api.getAssetsForAlbum(albumId: albumIdArg, updatedTimeCond: updatedTimeCondArg) { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -707,6 +710,19 @@ class NativeSyncApiSetup {
|
||||
} else {
|
||||
cancelHashingChannel.setMessageHandler(nil)
|
||||
}
|
||||
let cancelSyncChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.cancelSync\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
cancelSyncChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
try api.cancelSync()
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cancelSyncChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getTrashedAssetsChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
|
||||
@@ -39,6 +39,9 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
private static let hashCancelledCode = "HASH_CANCELLED"
|
||||
private static let hashCancelled = Result<[HashResult], Error>.failure(PigeonError(code: hashCancelledCode, message: "Hashing cancelled", details: nil))
|
||||
|
||||
private var syncTask: Task<Void?, Error>?
|
||||
private static let syncCancelledCode = "SYNC_CANCELLED"
|
||||
private static let syncCancelled = PigeonError(code: syncCancelledCode, message: "Sync cancelled", details: nil)
|
||||
|
||||
init(with defaults: UserDefaults = .standard) {
|
||||
self.defaults = defaults
|
||||
@@ -71,7 +74,11 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
saveChangeToken(token: PHPhotoLibrary.shared().currentChangeToken)
|
||||
}
|
||||
|
||||
func shouldFullSync() -> Bool {
|
||||
func shouldFullSync(completion: @escaping (Result<Bool, Error>) -> Void) {
|
||||
runSync(completion) { $0.shouldFullSync() }
|
||||
}
|
||||
|
||||
private func shouldFullSync() -> Bool {
|
||||
guard #available(iOS 16, *),
|
||||
PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized,
|
||||
let storedToken = getChangeToken() else {
|
||||
@@ -87,12 +94,17 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
return false
|
||||
}
|
||||
|
||||
func getAlbums() throws -> [PlatformAlbum] {
|
||||
func getAlbums(completion: @escaping (Result<[PlatformAlbum], Error>) -> Void) {
|
||||
runSync(completion) { try $0.getAlbums() }
|
||||
}
|
||||
|
||||
private func getAlbums() throws -> [PlatformAlbum] {
|
||||
var albums: [PlatformAlbum] = []
|
||||
|
||||
albumTypes.forEach { type in
|
||||
for type in albumTypes {
|
||||
let collections = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: nil)
|
||||
for i in 0..<collections.count {
|
||||
try Task.checkCancellation()
|
||||
let album = collections.object(at: i)
|
||||
|
||||
// Ignore recovered album
|
||||
@@ -126,7 +138,11 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
return albums.sorted { $0.id < $1.id }
|
||||
}
|
||||
|
||||
func getMediaChanges() throws -> SyncDelta {
|
||||
func getMediaChanges(completion: @escaping (Result<SyncDelta, Error>) -> Void) {
|
||||
runSync(completion) { try $0.getMediaChanges() }
|
||||
}
|
||||
|
||||
private func getMediaChanges() throws -> SyncDelta {
|
||||
guard #available(iOS 16, *) else {
|
||||
throw PigeonError(code: "UNSUPPORTED_OS", message: "This feature requires iOS 16 or later.", details: nil)
|
||||
}
|
||||
@@ -146,51 +162,49 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
return SyncDelta(hasChanges: false, updates: [], deletes: [], assetAlbums: [:])
|
||||
}
|
||||
|
||||
do {
|
||||
let changes = try PHPhotoLibrary.shared().fetchPersistentChanges(since: storedToken)
|
||||
let changes = try PHPhotoLibrary.shared().fetchPersistentChanges(since: storedToken)
|
||||
|
||||
var updatedAssets: Set<AssetWrapper> = []
|
||||
var deletedAssets: Set<String> = []
|
||||
|
||||
for change in changes {
|
||||
try Task.checkCancellation()
|
||||
guard let details = try? change.changeDetails(for: PHObjectType.asset) else { continue }
|
||||
|
||||
var updatedAssets: Set<AssetWrapper> = []
|
||||
var deletedAssets: Set<String> = []
|
||||
let updated = details.updatedLocalIdentifiers.union(details.insertedLocalIdentifiers)
|
||||
deletedAssets.formUnion(details.deletedLocalIdentifiers)
|
||||
|
||||
for change in changes {
|
||||
guard let details = try? change.changeDetails(for: PHObjectType.asset) else { continue }
|
||||
if (updated.isEmpty) { continue }
|
||||
|
||||
let options = PHFetchOptions()
|
||||
options.includeHiddenAssets = false
|
||||
let result = PHAsset.fetchAssets(withLocalIdentifiers: Array(updated), options: options)
|
||||
for i in 0..<result.count {
|
||||
let asset = result.object(at: i)
|
||||
|
||||
let updated = details.updatedLocalIdentifiers.union(details.insertedLocalIdentifiers)
|
||||
deletedAssets.formUnion(details.deletedLocalIdentifiers)
|
||||
|
||||
if (updated.isEmpty) { continue }
|
||||
|
||||
let options = PHFetchOptions()
|
||||
options.includeHiddenAssets = false
|
||||
let result = PHAsset.fetchAssets(withLocalIdentifiers: Array(updated), options: options)
|
||||
for i in 0..<result.count {
|
||||
let asset = result.object(at: i)
|
||||
|
||||
// Asset wrapper only uses the id for comparison. Multiple change can contain the same asset, skip duplicate changes
|
||||
let predicate = PlatformAsset(
|
||||
id: asset.localIdentifier,
|
||||
name: "",
|
||||
type: 0,
|
||||
durationMs: 0,
|
||||
orientation: 0,
|
||||
isFavorite: false,
|
||||
playbackStyle: .unknown
|
||||
)
|
||||
if (updatedAssets.contains(AssetWrapper(with: predicate))) {
|
||||
continue
|
||||
}
|
||||
|
||||
let domainAsset = AssetWrapper(with: asset.toPlatformAsset())
|
||||
updatedAssets.insert(domainAsset)
|
||||
// Asset wrapper only uses the id for comparison. Multiple change can contain the same asset, skip duplicate changes
|
||||
let predicate = PlatformAsset(
|
||||
id: asset.localIdentifier,
|
||||
name: "",
|
||||
type: 0,
|
||||
durationMs: 0,
|
||||
orientation: 0,
|
||||
isFavorite: false,
|
||||
playbackStyle: .unknown
|
||||
)
|
||||
if (updatedAssets.contains(AssetWrapper(with: predicate))) {
|
||||
continue
|
||||
}
|
||||
|
||||
let domainAsset = AssetWrapper(with: asset.toPlatformAsset())
|
||||
updatedAssets.insert(domainAsset)
|
||||
}
|
||||
|
||||
let updates = Array(updatedAssets.map { $0.asset })
|
||||
return SyncDelta(hasChanges: true, updates: updates, deletes: Array(deletedAssets), assetAlbums: buildAssetAlbumsMap(assets: updates))
|
||||
}
|
||||
|
||||
let updates = Array(updatedAssets.map { $0.asset })
|
||||
return SyncDelta(hasChanges: true, updates: updates, deletes: Array(deletedAssets), assetAlbums: buildAssetAlbumsMap(assets: updates))
|
||||
}
|
||||
|
||||
|
||||
private func buildAssetAlbumsMap(assets: Array<PlatformAsset>) -> [String: [String]] {
|
||||
guard !assets.isEmpty else {
|
||||
return [:]
|
||||
@@ -213,7 +227,11 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
return albumAssets
|
||||
}
|
||||
|
||||
func getAssetIdsForAlbum(albumId: String) throws -> [String] {
|
||||
func getAssetIdsForAlbum(albumId: String, completion: @escaping (Result<[String], Error>) -> Void) {
|
||||
runSync(completion) { try $0.getAssetIdsForAlbum(albumId: albumId) }
|
||||
}
|
||||
|
||||
private func getAssetIdsForAlbum(albumId: String) throws -> [String] {
|
||||
let collections = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [albumId], options: nil)
|
||||
guard let album = collections.firstObject else {
|
||||
return []
|
||||
@@ -223,9 +241,14 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
let options = PHFetchOptions()
|
||||
options.includeHiddenAssets = false
|
||||
let assets = getAssetsFromAlbum(in: album, options: options)
|
||||
assets.enumerateObjects { (asset, _, _) in
|
||||
assets.enumerateObjects { (asset, _, stop) in
|
||||
if Task.isCancelled {
|
||||
stop.pointee = true
|
||||
return
|
||||
}
|
||||
ids.append(asset.localIdentifier)
|
||||
}
|
||||
try Task.checkCancellation()
|
||||
return ids
|
||||
}
|
||||
|
||||
@@ -243,7 +266,11 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
return Int64(assets.count)
|
||||
}
|
||||
|
||||
func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset] {
|
||||
func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?, completion: @escaping (Result<[PlatformAsset], Error>) -> Void) {
|
||||
runSync(completion) { try $0.getAssetsForAlbum(albumId: albumId, updatedTimeCond: updatedTimeCond) }
|
||||
}
|
||||
|
||||
private func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset] {
|
||||
let collections = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [albumId], options: nil)
|
||||
guard let album = collections.firstObject else {
|
||||
return []
|
||||
@@ -262,9 +289,14 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
}
|
||||
|
||||
var assets: [PlatformAsset] = []
|
||||
result.enumerateObjects { (asset, _, _) in
|
||||
result.enumerateObjects { (asset, _, stop) in
|
||||
if Task.isCancelled {
|
||||
stop.pointee = true
|
||||
return
|
||||
}
|
||||
assets.append(asset.toPlatformAsset())
|
||||
}
|
||||
try Task.checkCancellation()
|
||||
return assets
|
||||
}
|
||||
|
||||
@@ -324,6 +356,31 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
||||
hashTask = nil
|
||||
}
|
||||
|
||||
func cancelSync() {
|
||||
syncTask?.cancel()
|
||||
syncTask = nil
|
||||
}
|
||||
|
||||
private func runSync<T>(
|
||||
_ completion: @escaping (Result<T, Error>) -> Void,
|
||||
_ work: @escaping (NativeSyncApiImpl) throws -> T
|
||||
) {
|
||||
syncTask?.cancel()
|
||||
syncTask = Task { [weak self] in
|
||||
guard let self else { return nil }
|
||||
let result: Result<T, Error>
|
||||
do {
|
||||
result = .success(try work(self))
|
||||
} catch is CancellationError {
|
||||
result = .failure(Self.syncCancelled)
|
||||
} catch {
|
||||
result = .failure(error)
|
||||
}
|
||||
self.completeWhenActive(for: completion, with: result)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func hashAsset(_ asset: PHAsset, allowNetworkAccess: Bool) async -> HashResult? {
|
||||
class RequestRef {
|
||||
var id: PHAssetResourceDataRequestID?
|
||||
|
||||
@@ -188,20 +188,14 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
||||
if (!_cancellationToken.isCompleted) {
|
||||
_cancellationToken.complete();
|
||||
}
|
||||
final cleanupFutures = [
|
||||
nativeSyncApi?.cancelHashing(),
|
||||
workerManagerPatch.dispose().catchError((_) async {
|
||||
// Discard any errors on the dispose call
|
||||
return;
|
||||
}),
|
||||
LogService.I.dispose(),
|
||||
Store.dispose(),
|
||||
|
||||
backgroundSyncManager?.cancel(),
|
||||
_drift.optimize(allTables: true),
|
||||
];
|
||||
|
||||
await Future.wait(cleanupFutures.nonNulls);
|
||||
// Workers share one sqlite connection, so DB teardown must wait until every worker has stopped using it.
|
||||
await Future.wait([
|
||||
if (backgroundSyncManager != null) backgroundSyncManager.cancel(),
|
||||
if (nativeSyncApi != null) nativeSyncApi.cancelHashing(),
|
||||
]);
|
||||
await workerManagerPatch.dispose().catchError((_) async {});
|
||||
await Future.wait([LogService.I.dispose(), Store.dispose(), _drift.optimize(allTables: true)]);
|
||||
await _drift.close();
|
||||
await _driftLogger.close();
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||
@@ -17,7 +19,7 @@ class HashService {
|
||||
final DriftLocalAssetRepository _localAssetRepository;
|
||||
final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository;
|
||||
final NativeSyncApi _nativeSyncApi;
|
||||
final bool Function()? _cancelChecker;
|
||||
final Completer<void>? _cancellation;
|
||||
final _log = Logger('HashService');
|
||||
|
||||
HashService({
|
||||
@@ -25,11 +27,15 @@ class HashService {
|
||||
required this._localAssetRepository,
|
||||
required this._trashedLocalAssetRepository,
|
||||
required this._nativeSyncApi,
|
||||
this._cancelChecker,
|
||||
this._cancellation,
|
||||
int? batchSize,
|
||||
}) : _batchSize = batchSize ?? kBatchHashFileLimit;
|
||||
}) : _batchSize = batchSize ?? kBatchHashFileLimit {
|
||||
// Stop the in-flight native hash call promptly on cancellation; the loops
|
||||
// below also observe [isCancelled] to bail between batches.
|
||||
_cancellation?.future.then((_) => _nativeSyncApi.cancelHashing().onError(_log.warning));
|
||||
}
|
||||
|
||||
bool get isCancelled => _cancelChecker?.call() ?? false;
|
||||
bool get isCancelled => _cancellation?.isCompleted ?? false;
|
||||
|
||||
Future<void> hashAssets() async {
|
||||
_log.info("Starting hashing of assets");
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.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/store.model.dart';
|
||||
@@ -17,6 +18,8 @@ import 'package:immich_mobile/utils/datetime_helpers.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
const String _kSyncCancelledCode = "SYNC_CANCELLED";
|
||||
|
||||
class LocalSyncService {
|
||||
final DriftLocalAlbumRepository _localAlbumRepository;
|
||||
// ignore: unused_field
|
||||
@@ -25,6 +28,7 @@ class LocalSyncService {
|
||||
final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository;
|
||||
final AssetMediaRepository _assetMediaRepository;
|
||||
final IPermissionRepository _permissionRepository;
|
||||
final Completer<void>? _cancellation;
|
||||
final Logger _log = Logger("DeviceSyncService");
|
||||
|
||||
LocalSyncService({
|
||||
@@ -34,7 +38,12 @@ class LocalSyncService {
|
||||
required this._trashedLocalAssetRepository,
|
||||
required this._assetMediaRepository,
|
||||
required this._permissionRepository,
|
||||
});
|
||||
this._cancellation,
|
||||
}) {
|
||||
_cancellation?.future.then((_) => _nativeSyncApi.cancelSync().onError(_log.warning));
|
||||
}
|
||||
|
||||
bool get _isCancelled => _cancellation?.isCompleted ?? false;
|
||||
|
||||
Future<void> sync({bool full = false}) async {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
@@ -81,6 +90,10 @@ class LocalSyncService {
|
||||
// detect album deletions from the native side
|
||||
if (CurrentPlatform.isAndroid) {
|
||||
for (final album in dbAlbums) {
|
||||
if (_isCancelled) {
|
||||
_log.warning("Local sync cancelled. Stopped processing albums.");
|
||||
return;
|
||||
}
|
||||
final deviceIds = await _nativeSyncApi.getAssetIdsForAlbum(album.id);
|
||||
await _localAlbumRepository.syncDeletes(album.id, deviceIds);
|
||||
}
|
||||
@@ -91,6 +104,10 @@ class LocalSyncService {
|
||||
// does not include changes for cloud albums.
|
||||
final cloudAlbums = deviceAlbums.where((a) => a.isCloud).toLocalAlbums();
|
||||
for (final album in cloudAlbums) {
|
||||
if (_isCancelled) {
|
||||
_log.warning("Local sync cancelled. Stopped processing cloud albums.");
|
||||
return;
|
||||
}
|
||||
final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id);
|
||||
if (dbAlbum == null) {
|
||||
_log.warning("Cloud album ${album.name} not found in local database. Skipping sync.");
|
||||
@@ -102,6 +119,12 @@ class LocalSyncService {
|
||||
await _mapIosCloudIds(newAssets);
|
||||
}
|
||||
await _nativeSyncApi.checkpointSync();
|
||||
} on PlatformException catch (e, s) {
|
||||
if (e.code == _kSyncCancelledCode) {
|
||||
_log.warning("Local sync cancelled");
|
||||
} else {
|
||||
_log.severe("Error performing device sync", e, s);
|
||||
}
|
||||
} catch (e, s) {
|
||||
_log.severe("Error performing device sync", e, s);
|
||||
} finally {
|
||||
@@ -129,12 +152,21 @@ class LocalSyncService {
|
||||
await _nativeSyncApi.checkpointSync();
|
||||
stopwatch.stop();
|
||||
_log.info("Full device sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
} on PlatformException catch (e, s) {
|
||||
if (e.code == _kSyncCancelledCode) {
|
||||
_log.warning("Full device sync cancelled");
|
||||
} else {
|
||||
_log.severe("Error performing full device sync", e, s);
|
||||
}
|
||||
} catch (e, s) {
|
||||
_log.severe("Error performing full device sync", e, s);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addAlbum(LocalAlbum album) async {
|
||||
if (_isCancelled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
_log.fine("Adding device album ${album.name}");
|
||||
|
||||
@@ -162,6 +194,9 @@ class LocalSyncService {
|
||||
|
||||
// The deviceAlbum is ignored since we are going to refresh it anyways
|
||||
FutureOr<bool> updateAlbum(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async {
|
||||
if (_isCancelled) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
_log.fine("Syncing device album ${dbAlbum.name}");
|
||||
|
||||
|
||||
@@ -112,10 +112,16 @@ class LogService {
|
||||
return _flushBuffer();
|
||||
}
|
||||
|
||||
Future<void> dispose() {
|
||||
Future<void> dispose() async {
|
||||
_flushTimer?.cancel();
|
||||
_logSubscription.cancel();
|
||||
return _flushBuffer();
|
||||
_flushTimer = null;
|
||||
await _logSubscription.cancel();
|
||||
await _flushBuffer();
|
||||
// Allow a subsequent init() (e.g. when a worker isolate is reused) to
|
||||
// create a fresh instance instead of returning this disposed one.
|
||||
if (identical(_instance, this)) {
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _flushBuffer() async {
|
||||
|
||||
@@ -54,7 +54,13 @@ class StoreService {
|
||||
/// Disposes the store and cancels the subscription. To reuse the store call init() again
|
||||
Future<void> dispose() async {
|
||||
await _storeUpdateSubscription?.cancel();
|
||||
_storeUpdateSubscription = null;
|
||||
_cache.clear();
|
||||
// Allow a subsequent init() (e.g. when a worker isolate is reused) to
|
||||
// create a fresh instance instead of returning this disposed one.
|
||||
if (identical(_instance, this)) {
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the cached value for [key], or `null`
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
@@ -5,6 +7,7 @@ import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
|
||||
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||
import 'package:immich_mobile/utils/debug_print.dart';
|
||||
@@ -16,6 +19,7 @@ final syncLinkedAlbumServiceProvider = Provider(
|
||||
ref.watch(remoteAlbumRepository),
|
||||
ref.watch(driftAlbumApiRepositoryProvider),
|
||||
ref.watch(storeServiceProvider),
|
||||
cancellation: ref.watch(cancellationProvider),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -24,13 +28,15 @@ class SyncLinkedAlbumService {
|
||||
final DriftRemoteAlbumRepository _remoteAlbumRepository;
|
||||
final DriftAlbumApiRepository _albumApiRepository;
|
||||
final StoreService _storeService;
|
||||
final Completer<void>? _cancellation;
|
||||
|
||||
SyncLinkedAlbumService(
|
||||
this._localAlbumRepository,
|
||||
this._remoteAlbumRepository,
|
||||
this._albumApiRepository,
|
||||
this._storeService,
|
||||
);
|
||||
this._storeService, {
|
||||
this._cancellation,
|
||||
});
|
||||
|
||||
final _log = Logger("SyncLinkedAlbumService");
|
||||
|
||||
@@ -55,7 +61,11 @@ class SyncLinkedAlbumService {
|
||||
final assetIds = await _remoteAlbumRepository.getLinkedAssetIds(userId, localAlbum.id, linkedRemoteAlbumId);
|
||||
_log.fine("Syncing ${assetIds.length} assets to remote album: ${remoteAlbum.name}");
|
||||
if (assetIds.isNotEmpty) {
|
||||
final album = await _albumApiRepository.addAssets(remoteAlbum.id, assetIds);
|
||||
final album = await _albumApiRepository.addAssets(
|
||||
remoteAlbum.id,
|
||||
assetIds,
|
||||
abortTrigger: _cancellation?.future,
|
||||
);
|
||||
await _remoteAlbumRepository.addAssets(remoteAlbum.id, album.added);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -38,7 +38,7 @@ class SyncStreamService {
|
||||
final IPermissionRepository _permissionRepository;
|
||||
final SyncMigrationRepository _syncMigrationRepository;
|
||||
final ApiService _api;
|
||||
final bool Function()? _cancelChecker;
|
||||
final Completer<void>? _cancellation;
|
||||
|
||||
SyncStreamService({
|
||||
required this._syncApiRepository,
|
||||
@@ -49,10 +49,10 @@ class SyncStreamService {
|
||||
required this._permissionRepository,
|
||||
required this._syncMigrationRepository,
|
||||
required this._api,
|
||||
this._cancelChecker,
|
||||
this._cancellation,
|
||||
});
|
||||
|
||||
bool get isCancelled => _cancelChecker?.call() ?? false;
|
||||
bool get isCancelled => _cancellation?.isCompleted ?? false;
|
||||
|
||||
Future<bool> sync() async {
|
||||
_logger.info("Remote sync request for user");
|
||||
@@ -80,10 +80,15 @@ class SyncStreamService {
|
||||
_handleEvents,
|
||||
serverVersion: serverSemVer,
|
||||
onReset: () => shouldReset = true,
|
||||
abortSignal: _cancellation?.future,
|
||||
);
|
||||
if (shouldReset) {
|
||||
_logger.info("Resetting sync state as requested by server");
|
||||
await _syncApiRepository.streamChanges(_handleEvents, serverVersion: serverSemVer);
|
||||
await _syncApiRepository.streamChanges(
|
||||
_handleEvents,
|
||||
serverVersion: serverSemVer,
|
||||
abortSignal: _cancellation?.future,
|
||||
);
|
||||
}
|
||||
|
||||
previousLength = migrations.length;
|
||||
@@ -318,7 +323,7 @@ class SyncStreamService {
|
||||
}
|
||||
|
||||
Future<void> handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) async {
|
||||
if (batchData.isEmpty) {
|
||||
if (batchData.isEmpty || isCancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -361,7 +366,7 @@ class SyncStreamService {
|
||||
}
|
||||
|
||||
Future<void> handleWsAssetUploadReadyV2Batch(List<dynamic> batchData) async {
|
||||
if (batchData.isEmpty) {
|
||||
if (batchData.isEmpty || isCancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -404,6 +409,9 @@ class SyncStreamService {
|
||||
}
|
||||
|
||||
Future<void> handleWsAssetEditReadyV1(dynamic data) async {
|
||||
if (isCancelled) {
|
||||
return;
|
||||
}
|
||||
_logger.info('Processing AssetEditReadyV1 event');
|
||||
|
||||
try {
|
||||
@@ -444,6 +452,9 @@ class SyncStreamService {
|
||||
}
|
||||
|
||||
Future<void> handleWsAssetEditReadyV2(dynamic data) async {
|
||||
if (isCancelled) {
|
||||
return;
|
||||
}
|
||||
_logger.info('Processing AssetEditReadyV2 event');
|
||||
|
||||
try {
|
||||
|
||||
@@ -50,53 +50,27 @@ class BackgroundSyncManager {
|
||||
});
|
||||
|
||||
Future<void> cancel() async {
|
||||
final futures = <Future>[];
|
||||
|
||||
if (_syncTask != null) {
|
||||
futures.add(_syncTask!.future);
|
||||
final tasks = [
|
||||
_syncTask,
|
||||
_syncWebsocketTask,
|
||||
_cloudIdSyncTask,
|
||||
_linkedAlbumSyncTask,
|
||||
_deviceAlbumSyncTask,
|
||||
_hashTask,
|
||||
];
|
||||
final futures = [
|
||||
for (final task in tasks)
|
||||
if (task != null) task.future,
|
||||
];
|
||||
for (final task in tasks) {
|
||||
task?.cancel();
|
||||
}
|
||||
_syncTask?.cancel();
|
||||
_syncTask = null;
|
||||
|
||||
if (_syncWebsocketTask != null) {
|
||||
futures.add(_syncWebsocketTask!.future);
|
||||
}
|
||||
_syncWebsocketTask?.cancel();
|
||||
_syncWebsocketTask = null;
|
||||
|
||||
if (_cloudIdSyncTask != null) {
|
||||
futures.add(_cloudIdSyncTask!.future);
|
||||
}
|
||||
_cloudIdSyncTask?.cancel();
|
||||
_cloudIdSyncTask = null;
|
||||
|
||||
if (_linkedAlbumSyncTask != null) {
|
||||
futures.add(_linkedAlbumSyncTask!.future);
|
||||
}
|
||||
_linkedAlbumSyncTask?.cancel();
|
||||
_linkedAlbumSyncTask = null;
|
||||
|
||||
try {
|
||||
await Future.wait(futures);
|
||||
} on CanceledError {
|
||||
// Ignore cancellation errors
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cancelLocal() async {
|
||||
final futures = <Future>[];
|
||||
|
||||
if (_hashTask != null) {
|
||||
futures.add(_hashTask!.future);
|
||||
}
|
||||
_hashTask?.cancel();
|
||||
_hashTask = null;
|
||||
|
||||
if (_deviceAlbumSyncTask != null) {
|
||||
futures.add(_deviceAlbumSyncTask!.future);
|
||||
}
|
||||
_deviceAlbumSyncTask?.cancel();
|
||||
_deviceAlbumSyncTask = null;
|
||||
_hashTask = null;
|
||||
|
||||
try {
|
||||
await Future.wait(futures);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
@@ -9,6 +11,7 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.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/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||
@@ -51,9 +54,10 @@ Future<void> syncCloudIds(ProviderContainer ref) async {
|
||||
}
|
||||
|
||||
final assetApi = ref.read(apiServiceProvider).assetsApi;
|
||||
final cancellation = ref.read(cancellationProvider);
|
||||
|
||||
// Process cloud IDs in paginated batches
|
||||
await _processCloudIdMappingsInBatches(db, currentUser.id, assetApi, canBulkUpdateMetadata, logger);
|
||||
await _processCloudIdMappingsInBatches(db, currentUser.id, assetApi, canBulkUpdateMetadata, logger, cancellation);
|
||||
}
|
||||
|
||||
Future<void> _processCloudIdMappingsInBatches(
|
||||
@@ -62,12 +66,17 @@ Future<void> _processCloudIdMappingsInBatches(
|
||||
AssetsApi assetsApi,
|
||||
bool canBulkUpdate,
|
||||
Logger logger,
|
||||
Completer<void> cancellation,
|
||||
) async {
|
||||
const pageSize = 20000;
|
||||
String? lastLocalId;
|
||||
final seenRemoteAssetIds = <String>{};
|
||||
|
||||
while (true) {
|
||||
if (cancellation.isCompleted) {
|
||||
logger.warning('Cloud ID migration cancelled. Stopping batch processing.');
|
||||
break;
|
||||
}
|
||||
final mappings = await _fetchCloudIdMappings(drift, userId, pageSize, lastLocalId);
|
||||
if (mappings.isEmpty) {
|
||||
break;
|
||||
@@ -98,9 +107,9 @@ Future<void> _processCloudIdMappingsInBatches(
|
||||
|
||||
if (items.isNotEmpty) {
|
||||
if (canBulkUpdate) {
|
||||
await _bulkUpdateCloudIds(assetsApi, items);
|
||||
await _bulkUpdateCloudIds(assetsApi, items, cancellation.future);
|
||||
} else {
|
||||
await _sequentialUpdateCloudIds(assetsApi, items);
|
||||
await _sequentialUpdateCloudIds(assetsApi, items, cancellation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,20 +120,35 @@ Future<void> _processCloudIdMappingsInBatches(
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _sequentialUpdateCloudIds(AssetsApi assetsApi, List<AssetMetadataBulkUpsertItemDto> items) async {
|
||||
Future<void> _sequentialUpdateCloudIds(
|
||||
AssetsApi assetsApi,
|
||||
List<AssetMetadataBulkUpsertItemDto> items,
|
||||
Completer<void> cancellation,
|
||||
) async {
|
||||
for (final item in items) {
|
||||
if (cancellation.isCompleted) {
|
||||
break;
|
||||
}
|
||||
final upsertItem = AssetMetadataUpsertItemDto(key: item.key, value: item.value);
|
||||
try {
|
||||
await assetsApi.updateAssetMetadata(item.assetId, AssetMetadataUpsertDto(items: [upsertItem]));
|
||||
await assetsApi.updateAssetMetadata(
|
||||
item.assetId,
|
||||
AssetMetadataUpsertDto(items: [upsertItem]),
|
||||
abortTrigger: cancellation.future,
|
||||
);
|
||||
} catch (error, stack) {
|
||||
Logger('migrateCloudIds').warning('Failed to update metadata for asset ${item.assetId}', error, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _bulkUpdateCloudIds(AssetsApi assetsApi, List<AssetMetadataBulkUpsertItemDto> items) async {
|
||||
Future<void> _bulkUpdateCloudIds(
|
||||
AssetsApi assetsApi,
|
||||
List<AssetMetadataBulkUpsertItemDto> items,
|
||||
Future<void> abortTrigger,
|
||||
) async {
|
||||
try {
|
||||
await assetsApi.updateBulkAssetMetadata(AssetMetadataBulkUpsertDto(items: items));
|
||||
await assetsApi.updateBulkAssetMetadata(AssetMetadataBulkUpsertDto(items: items), abortTrigger: abortTrigger);
|
||||
} catch (error, stack) {
|
||||
Logger('migrateCloudIds').warning('Failed to bulk update metadata', error, stack);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ extension DTOToAsset on api.AssetResponseDto {
|
||||
height: height?.toInt(),
|
||||
width: width?.toInt(),
|
||||
isFavorite: isFavorite,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
livePhotoVideoId: livePhotoVideoId.orElse(null),
|
||||
thumbHash: thumbhash,
|
||||
localId: null,
|
||||
type: type.toAssetType(),
|
||||
stackId: stack?.id,
|
||||
stackId: stack.orElse(null)?.id,
|
||||
isEdited: isEdited,
|
||||
);
|
||||
}
|
||||
@@ -41,13 +41,13 @@ extension DTOToAsset on api.AssetResponseDto {
|
||||
height: height?.toInt(),
|
||||
width: width?.toInt(),
|
||||
isFavorite: isFavorite,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
livePhotoVideoId: livePhotoVideoId.orElse(null),
|
||||
thumbHash: thumbhash,
|
||||
localId: null,
|
||||
type: type.toAssetType(),
|
||||
stackId: stack?.id,
|
||||
stackId: stack.orElse(null)?.id,
|
||||
isEdited: isEdited,
|
||||
exifInfo: exifInfo != null ? ExifDtoConverter.fromDto(exifInfo!) : const ExifInfo(),
|
||||
exifInfo: exifInfo.orElse(null) != null ? ExifDtoConverter.fromDto(exifInfo.orElse(null)!) : const ExifInfo(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,50 +20,64 @@ class SearchApiRepository extends ApiRepository {
|
||||
(filter.assetId != null && filter.assetId!.isNotEmpty)) {
|
||||
return _api.searchSmart(
|
||||
SmartSearchDto(
|
||||
query: filter.context,
|
||||
queryAssetId: filter.assetId,
|
||||
language: filter.language,
|
||||
country: filter.location.country,
|
||||
state: filter.location.state,
|
||||
city: filter.location.city,
|
||||
make: filter.camera.make,
|
||||
model: filter.camera.model,
|
||||
takenAfter: filter.date.takenAfter,
|
||||
takenBefore: filter.date.takenBefore,
|
||||
visibility: filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline,
|
||||
rating: filter.rating.rating,
|
||||
isFavorite: filter.display.isFavorite ? true : null,
|
||||
isNotInAlbum: filter.display.isNotInAlbum ? true : null,
|
||||
personIds: filter.people.map((e) => e.id).toList(),
|
||||
tagIds: filter.tagIds,
|
||||
type: type,
|
||||
page: page,
|
||||
size: 100,
|
||||
query: filter.context == null ? const Optional.absent() : Optional.present(filter.context!),
|
||||
queryAssetId: filter.assetId == null ? const Optional.absent() : Optional.present(filter.assetId!),
|
||||
language: filter.language == null ? const Optional.absent() : Optional.present(filter.language!),
|
||||
country: filter.location.country == null
|
||||
? const Optional.absent()
|
||||
: Optional.present(filter.location.country!),
|
||||
state: filter.location.state == null ? const Optional.absent() : Optional.present(filter.location.state!),
|
||||
city: filter.location.city == null ? const Optional.absent() : Optional.present(filter.location.city!),
|
||||
make: filter.camera.make == null ? const Optional.absent() : Optional.present(filter.camera.make!),
|
||||
model: filter.camera.model == null ? const Optional.absent() : Optional.present(filter.camera.model!),
|
||||
takenAfter: filter.date.takenAfter == null
|
||||
? const Optional.absent()
|
||||
: Optional.present(filter.date.takenAfter!),
|
||||
takenBefore: filter.date.takenBefore == null
|
||||
? const Optional.absent()
|
||||
: Optional.present(filter.date.takenBefore!),
|
||||
visibility: Optional.present(filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline),
|
||||
rating: filter.rating.rating == null ? const Optional.absent() : Optional.present(filter.rating.rating!),
|
||||
isFavorite: filter.display.isFavorite ? const Optional.present(true) : const Optional.absent(),
|
||||
isNotInAlbum: filter.display.isNotInAlbum ? const Optional.present(true) : const Optional.absent(),
|
||||
personIds: Optional.present(filter.people.map((e) => e.id).toList()),
|
||||
tagIds: filter.tagIds == null ? const Optional.absent() : Optional.present(filter.tagIds!),
|
||||
type: type == null ? const Optional.absent() : Optional.present(type),
|
||||
page: Optional.present(page),
|
||||
size: const Optional.present(100),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return _api.searchAssets(
|
||||
MetadataSearchDto(
|
||||
originalFileName: filter.filename != null && filter.filename!.isNotEmpty ? filter.filename : null,
|
||||
country: filter.location.country,
|
||||
description: filter.description != null && filter.description!.isNotEmpty ? filter.description : null,
|
||||
ocr: filter.ocr != null && filter.ocr!.isNotEmpty ? filter.ocr : null,
|
||||
state: filter.location.state,
|
||||
city: filter.location.city,
|
||||
make: filter.camera.make,
|
||||
model: filter.camera.model,
|
||||
takenAfter: filter.date.takenAfter,
|
||||
takenBefore: filter.date.takenBefore,
|
||||
visibility: filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline,
|
||||
rating: filter.rating.rating,
|
||||
isFavorite: filter.display.isFavorite ? true : null,
|
||||
isNotInAlbum: filter.display.isNotInAlbum ? true : null,
|
||||
personIds: filter.people.map((e) => e.id).toList(),
|
||||
tagIds: filter.tagIds,
|
||||
type: type,
|
||||
page: page,
|
||||
size: 1000,
|
||||
originalFileName: filter.filename != null && filter.filename!.isNotEmpty
|
||||
? Optional.present(filter.filename!)
|
||||
: const Optional.absent(),
|
||||
country: filter.location.country == null ? const Optional.absent() : Optional.present(filter.location.country!),
|
||||
description: filter.description != null && filter.description!.isNotEmpty
|
||||
? Optional.present(filter.description!)
|
||||
: const Optional.absent(),
|
||||
ocr: filter.ocr != null && filter.ocr!.isNotEmpty ? Optional.present(filter.ocr!) : const Optional.absent(),
|
||||
state: filter.location.state == null ? const Optional.absent() : Optional.present(filter.location.state!),
|
||||
city: filter.location.city == null ? const Optional.absent() : Optional.present(filter.location.city!),
|
||||
make: filter.camera.make == null ? const Optional.absent() : Optional.present(filter.camera.make!),
|
||||
model: filter.camera.model == null ? const Optional.absent() : Optional.present(filter.camera.model!),
|
||||
takenAfter: filter.date.takenAfter == null
|
||||
? const Optional.absent()
|
||||
: Optional.present(filter.date.takenAfter!),
|
||||
takenBefore: filter.date.takenBefore == null
|
||||
? const Optional.absent()
|
||||
: Optional.present(filter.date.takenBefore!),
|
||||
visibility: Optional.present(filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline),
|
||||
rating: filter.rating.rating == null ? const Optional.absent() : Optional.present(filter.rating.rating!),
|
||||
isFavorite: filter.display.isFavorite ? const Optional.present(true) : const Optional.absent(),
|
||||
isNotInAlbum: filter.display.isNotInAlbum ? const Optional.present(true) : const Optional.absent(),
|
||||
personIds: Optional.present(filter.people.map((e) => e.id).toList()),
|
||||
tagIds: filter.tagIds == null ? const Optional.absent() : Optional.present(filter.tagIds!),
|
||||
type: type == null ? const Optional.absent() : Optional.present(type),
|
||||
page: Optional.present(page),
|
||||
size: const Optional.present(1000),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class SyncApiRepository {
|
||||
}
|
||||
|
||||
Future<void> deleteSyncAck(List<SyncEntityType> types) {
|
||||
return _api.syncApi.deleteSyncAck(SyncAckDeleteDto(types: types));
|
||||
return _api.syncApi.deleteSyncAck(SyncAckDeleteDto(types: Optional.present(types)));
|
||||
}
|
||||
|
||||
Future<void> streamChanges(
|
||||
@@ -29,6 +29,7 @@ class SyncApiRepository {
|
||||
Function()? onReset,
|
||||
int batchSize = kSyncEventBatchSize,
|
||||
http.Client? httpClient,
|
||||
Future<void>? abortSignal,
|
||||
}) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final client = httpClient ?? NetworkRepository.client;
|
||||
@@ -36,7 +37,7 @@ class SyncApiRepository {
|
||||
|
||||
final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'};
|
||||
|
||||
final request = http.Request('POST', Uri.parse(endpoint));
|
||||
final request = http.AbortableRequest('POST', Uri.parse(endpoint), abortTrigger: abortSignal);
|
||||
request.headers.addAll(headers);
|
||||
request.body = jsonEncode(
|
||||
SyncStreamDto(
|
||||
|
||||
@@ -91,7 +91,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
email: Value(user.email),
|
||||
hasProfileImage: Value(user.hasProfileImage),
|
||||
profileChangedAt: Value(user.profileChangedAt),
|
||||
avatarColor: Value(user.avatarColor?.toAvatarColor() ?? AvatarColor.primary),
|
||||
avatarColor: Value(user.avatarColor.orElse(null)?.toAvatarColor() ?? AvatarColor.primary),
|
||||
isAdmin: Value(user.isAdmin),
|
||||
pinCode: Value(user.pinCode),
|
||||
quotaSizeInBytes: Value(user.quotaSizeInBytes ?? 0),
|
||||
@@ -133,7 +133,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
email: Value(user.email),
|
||||
hasProfileImage: Value(user.hasProfileImage),
|
||||
profileChangedAt: Value(user.profileChangedAt),
|
||||
avatarColor: Value(user.avatarColor?.toAvatarColor() ?? AvatarColor.primary),
|
||||
avatarColor: Value(user.avatarColor.orElse(null)?.toAvatarColor() ?? AvatarColor.primary),
|
||||
);
|
||||
|
||||
batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion));
|
||||
|
||||
@@ -5,24 +5,24 @@ import 'package:openapi/api.dart';
|
||||
abstract final class ExifDtoConverter {
|
||||
static ExifInfo fromDto(ExifResponseDto dto) {
|
||||
return ExifInfo(
|
||||
fileSize: dto.fileSizeInByte,
|
||||
description: dto.description,
|
||||
orientation: dto.orientation,
|
||||
timeZone: dto.timeZone,
|
||||
dateTimeOriginal: dto.dateTimeOriginal,
|
||||
isFlipped: isOrientationFlipped(dto.orientation),
|
||||
latitude: dto.latitude?.toDouble(),
|
||||
longitude: dto.longitude?.toDouble(),
|
||||
city: dto.city,
|
||||
state: dto.state,
|
||||
country: dto.country,
|
||||
make: dto.make,
|
||||
model: dto.model,
|
||||
lens: dto.lensModel,
|
||||
f: dto.fNumber?.toDouble(),
|
||||
mm: dto.focalLength?.toDouble(),
|
||||
iso: dto.iso?.toInt(),
|
||||
exposureSeconds: exposureTimeToSeconds(dto.exposureTime),
|
||||
fileSize: dto.fileSizeInByte.orElse(null),
|
||||
description: dto.description.orElse(null),
|
||||
orientation: dto.orientation.orElse(null),
|
||||
timeZone: dto.timeZone.orElse(null),
|
||||
dateTimeOriginal: dto.dateTimeOriginal.orElse(null),
|
||||
isFlipped: isOrientationFlipped(dto.orientation.orElse(null)),
|
||||
latitude: dto.latitude.orElse(null)?.toDouble(),
|
||||
longitude: dto.longitude.orElse(null)?.toDouble(),
|
||||
city: dto.city.orElse(null),
|
||||
state: dto.state.orElse(null),
|
||||
country: dto.country.orElse(null),
|
||||
make: dto.make.orElse(null),
|
||||
model: dto.model.orElse(null),
|
||||
lens: dto.lensModel.orElse(null),
|
||||
f: dto.fNumber.orElse(null)?.toDouble(),
|
||||
mm: dto.focalLength.orElse(null)?.toDouble(),
|
||||
iso: dto.iso.orElse(null)?.toInt(),
|
||||
exposureSeconds: exposureTimeToSeconds(dto.exposureTime.orElse(null)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ abstract final class UserConverter {
|
||||
updatedAt: DateTime.now(),
|
||||
avatarColor: dto.avatarColor.toAvatarColor(),
|
||||
memoryEnabled: false,
|
||||
inTimeline: dto.inTimeline ?? false,
|
||||
inTimeline: dto.inTimeline.orElse(null) ?? false,
|
||||
isPartnerSharedBy: false,
|
||||
isPartnerSharedWith: false,
|
||||
profileChangedAt: dto.profileChangedAt,
|
||||
|
||||
@@ -73,10 +73,10 @@ class SharedLink {
|
||||
slug = dto.slug,
|
||||
type = dto.type == SharedLinkType.ALBUM ? SharedLinkSource.album : SharedLinkSource.individual,
|
||||
title = dto.type == SharedLinkType.ALBUM
|
||||
? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE"
|
||||
? dto.album.orElse(null)?.albumName.toUpperCase() ?? "UNKNOWN SHARE"
|
||||
: "INDIVIDUAL SHARE",
|
||||
thumbAssetId = dto.type == SharedLinkType.ALBUM
|
||||
? dto.album?.albumThumbnailAssetId
|
||||
? dto.album.orElse(null)?.albumThumbnailAssetId
|
||||
: dto.assets.isNotEmpty
|
||||
? dto.assets[0].id
|
||||
: null;
|
||||
|
||||
+14
@@ -635,6 +635,20 @@ class NativeSyncApi {
|
||||
_extractReplyValueOrThrow(pigeonVar_replyList, pigeonVar_channelName, isNullValid: true);
|
||||
}
|
||||
|
||||
Future<void> cancelSync() async {
|
||||
final pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.NativeSyncApi.cancelSync$pigeonVar_messageChannelSuffix';
|
||||
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
|
||||
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
|
||||
_extractReplyValueOrThrow(pigeonVar_replyList, pigeonVar_channelName, isNullValid: true);
|
||||
}
|
||||
|
||||
Future<Map<String, List<PlatformAsset>>> getTrashedAssets() async {
|
||||
final pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssets$pigeonVar_messageChannelSuffix';
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
/// Provider holding a boolean function that returns true when cancellation is requested.
|
||||
/// A computation running in the isolate uses the function to implement cooperative cancellation.
|
||||
final cancellationProvider = Provider<bool Function()>(
|
||||
/// Holds the isolate's cancellation signal.
|
||||
final cancellationProvider = Provider<Completer<void>>(
|
||||
// This will be overridden in the isolate's container.
|
||||
// Throwing ensures it's not used without an override.
|
||||
(ref) => throw UnimplementedError(
|
||||
|
||||
@@ -26,7 +26,7 @@ final syncStreamServiceProvider = Provider(
|
||||
permissionRepository: ref.watch(permissionRepositoryProvider),
|
||||
syncMigrationRepository: ref.watch(syncMigrationRepositoryProvider),
|
||||
api: ref.watch(apiServiceProvider),
|
||||
cancelChecker: ref.watch(cancellationProvider),
|
||||
cancellation: ref.watch(cancellationProvider),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -42,6 +42,7 @@ final localSyncServiceProvider = Provider(
|
||||
assetMediaRepository: ref.watch(assetMediaRepositoryProvider),
|
||||
permissionRepository: ref.watch(permissionRepositoryProvider),
|
||||
nativeSyncApi: ref.watch(nativeSyncApiProvider),
|
||||
cancellation: ref.watch(cancellationProvider),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -51,5 +52,6 @@ final hashServiceProvider = Provider(
|
||||
localAssetRepository: ref.watch(localAssetRepository),
|
||||
nativeSyncApi: ref.watch(nativeSyncApiProvider),
|
||||
trashedLocalAssetRepository: ref.watch(trashedLocalAssetRepository),
|
||||
cancellation: ref.watch(cancellationProvider),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ final getAllPlacesProvider = FutureProvider.autoDispose<List<SearchCuratedConten
|
||||
}
|
||||
|
||||
final curatedContent = assetPlaces
|
||||
.map((data) => SearchCuratedContent(label: data.exifInfo!.city!, id: data.id))
|
||||
.map((data) => SearchCuratedContent(label: data.exifInfo.orElse(null)!.city.orElse(null)!, id: data.id))
|
||||
.toList();
|
||||
|
||||
return curatedContent;
|
||||
|
||||
@@ -23,8 +23,8 @@ class ActivityApiRepository extends ApiRepository {
|
||||
final dto = ActivityCreateDto(
|
||||
albumId: albumId,
|
||||
type: type == ActivityType.comment ? ReactionType.comment : ReactionType.like,
|
||||
assetId: assetId,
|
||||
comment: comment,
|
||||
assetId: assetId == null ? const Optional.absent() : Optional.present(assetId),
|
||||
comment: comment == null ? const Optional.absent() : Optional.present(comment),
|
||||
);
|
||||
final response = await checkNull(_api.createActivity(dto));
|
||||
return _toActivity(response);
|
||||
@@ -45,6 +45,6 @@ class ActivityApiRepository extends ApiRepository {
|
||||
type: dto.type == ReactionType.comment ? ActivityType.comment : ActivityType.like,
|
||||
user: UserConverter.fromSimpleUserDto(dto.user),
|
||||
assetId: dto.assetId,
|
||||
comment: dto.comment,
|
||||
comment: dto.comment.orElse(null),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class AssetApiRepository extends ApiRepository {
|
||||
AssetApiRepository(this._api, this._stacksApi, this._trashApi);
|
||||
|
||||
Future<void> delete(List<String> ids, bool force) async {
|
||||
return _api.deleteAssets(AssetBulkDeleteDto(ids: ids, force: force));
|
||||
return _api.deleteAssets(AssetBulkDeleteDto(ids: ids, force: Optional.present(force)));
|
||||
}
|
||||
|
||||
Future<void> restoreTrash(List<String> ids) async {
|
||||
@@ -42,19 +42,27 @@ class AssetApiRepository extends ApiRepository {
|
||||
}
|
||||
|
||||
Future<void> updateVisibility(List<String> ids, AssetVisibilityEnum visibility) async {
|
||||
return _api.updateAssets(AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility)));
|
||||
return _api.updateAssets(AssetBulkUpdateDto(ids: ids, visibility: Optional.present(_mapVisibility(visibility))));
|
||||
}
|
||||
|
||||
Future<void> updateFavorite(List<String> ids, bool isFavorite) async {
|
||||
return _api.updateAssets(AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite));
|
||||
return _api.updateAssets(AssetBulkUpdateDto(ids: ids, isFavorite: Optional.present(isFavorite)));
|
||||
}
|
||||
|
||||
Future<void> updateLocation(List<String> ids, LatLng location) async {
|
||||
return _api.updateAssets(AssetBulkUpdateDto(ids: ids, latitude: location.latitude, longitude: location.longitude));
|
||||
return _api.updateAssets(
|
||||
AssetBulkUpdateDto(
|
||||
ids: ids,
|
||||
latitude: Optional.present(location.latitude),
|
||||
longitude: Optional.present(location.longitude),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateDateTime(List<String> ids, DateTime dateTime) async {
|
||||
return _api.updateAssets(AssetBulkUpdateDto(ids: ids, dateTimeOriginal: dateTime.toIso8601String()));
|
||||
return _api.updateAssets(
|
||||
AssetBulkUpdateDto(ids: ids, dateTimeOriginal: Optional.present(dateTime.toIso8601String())),
|
||||
);
|
||||
}
|
||||
|
||||
Future<StackResponse> stack(List<String> ids) async {
|
||||
@@ -82,15 +90,15 @@ class AssetApiRepository extends ApiRepository {
|
||||
final response = await checkNull(_api.getAssetInfo(assetId));
|
||||
|
||||
// we need to get the MIME of the thumbnail once that gets added to the API
|
||||
return response.originalMimeType;
|
||||
return response.originalMimeType.orElse(null);
|
||||
}
|
||||
|
||||
Future<void> updateDescription(String assetId, String description) {
|
||||
return _api.updateAsset(assetId, UpdateAssetDto(description: description));
|
||||
return _api.updateAsset(assetId, UpdateAssetDto(description: Optional.present(description)));
|
||||
}
|
||||
|
||||
Future<void> updateRating(String assetId, int rating) {
|
||||
return _api.updateAsset(assetId, UpdateAssetDto(rating: rating));
|
||||
return _api.updateAsset(assetId, UpdateAssetDto(rating: Optional.present(rating)));
|
||||
}
|
||||
|
||||
Future<AssetEditsResponseDto?> editAsset(String assetId, List<AssetEdit> edits) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class AuthApiRepository extends ApiRepository {
|
||||
AuthApiRepository(this._apiService);
|
||||
|
||||
Future<void> changePassword(String newPassword) async {
|
||||
await _apiService.usersApi.updateMyUser(UserUpdateMeDto(password: newPassword));
|
||||
await _apiService.usersApi.updateMyUser(UserUpdateMeDto(password: Optional.present(newPassword)));
|
||||
}
|
||||
|
||||
Future<LoginResponse> login(String email, String password) async {
|
||||
@@ -46,7 +46,7 @@ class AuthApiRepository extends ApiRepository {
|
||||
|
||||
Future<bool> unlockPinCode(String pinCode) async {
|
||||
try {
|
||||
await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: pinCode));
|
||||
await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: Optional.present(pinCode)));
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
|
||||
@@ -22,7 +22,13 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
String? description,
|
||||
}) async {
|
||||
final responseDto = await checkNull(
|
||||
_api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())),
|
||||
_api.createAlbum(
|
||||
CreateAlbumDto(
|
||||
albumName: name,
|
||||
description: description == null ? const Optional.absent() : Optional.present(description),
|
||||
assetIds: Optional.present(assetIds.toList()),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return responseDto.toRemoteAlbum(owner);
|
||||
@@ -41,8 +47,14 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
return (removed: removed, failed: failed);
|
||||
}
|
||||
|
||||
Future<({List<String> added, List<String> failed})> addAssets(String albumId, Iterable<String> assetIds) async {
|
||||
final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList())));
|
||||
Future<({List<String> added, List<String> failed})> addAssets(
|
||||
String albumId,
|
||||
Iterable<String> assetIds, {
|
||||
Future<void>? abortTrigger,
|
||||
}) async {
|
||||
final response = await checkNull(
|
||||
_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()), abortTrigger: abortTrigger),
|
||||
);
|
||||
final List<String> added = [], failed = [];
|
||||
for (final dto in response) {
|
||||
if (dto.success) {
|
||||
@@ -73,11 +85,13 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
_api.updateAlbumInfo(
|
||||
albumId,
|
||||
UpdateAlbumDto(
|
||||
albumName: name,
|
||||
description: description,
|
||||
albumThumbnailAssetId: thumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled,
|
||||
order: apiOrder,
|
||||
albumName: name == null ? const Optional.absent() : Optional.present(name),
|
||||
description: description == null ? const Optional.absent() : Optional.present(description),
|
||||
albumThumbnailAssetId: thumbnailAssetId == null
|
||||
? const Optional.absent()
|
||||
: Optional.present(thumbnailAssetId),
|
||||
isActivityEnabled: isActivityEnabled == null ? const Optional.absent() : Optional.present(isActivityEnabled),
|
||||
order: apiOrder == null ? const Optional.absent() : Optional.present(apiOrder),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -99,7 +113,9 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
}
|
||||
|
||||
Future<bool> setActivityStatus(String albumId, bool isEnabled) async {
|
||||
final response = await checkNull(_api.updateAlbumInfo(albumId, UpdateAlbumDto(isActivityEnabled: isEnabled)));
|
||||
final response = await checkNull(
|
||||
_api.updateAlbumInfo(albumId, UpdateAlbumDto(isActivityEnabled: Optional.present(isEnabled))),
|
||||
);
|
||||
return response.isActivityEnabled;
|
||||
}
|
||||
}
|
||||
@@ -116,7 +132,7 @@ extension on AlbumResponseDto {
|
||||
updatedAt: updatedAt,
|
||||
thumbnailAssetId: albumThumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled,
|
||||
order: order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc,
|
||||
order: order.orElse(null) == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc,
|
||||
assetCount: assetCount,
|
||||
isShared: albumUsers.length > 2,
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ class PartnerApiRepository extends ApiRepository {
|
||||
|
||||
Future<List<UserDto>> getAll(Direction direction) async {
|
||||
final response = await checkNull(
|
||||
_api.getPartners(direction == Direction.sharedByMe ? PartnerDirection.by : PartnerDirection.with_),
|
||||
_api.getPartners(direction == Direction.sharedByMe ? PartnerDirection.sharedBy : PartnerDirection.sharedWith),
|
||||
);
|
||||
return response.map(UserConverter.fromPartnerDto).toList();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ class PersonApiRepository extends ApiRepository {
|
||||
|
||||
Future<PersonDto> update(String id, {String? name, DateTime? birthday}) async {
|
||||
final birthdayUtc = birthday == null ? null : DateTime.utc(birthday.year, birthday.month, birthday.day);
|
||||
final dto = PersonUpdateDto(name: name, birthDate: birthdayUtc);
|
||||
final dto = PersonUpdateDto(
|
||||
name: name == null ? const Optional.absent() : Optional.present(name),
|
||||
birthDate: birthdayUtc == null ? const Optional.absent() : Optional.present(birthdayUtc),
|
||||
);
|
||||
final response = await checkNull(_api.updatePerson(id, dto));
|
||||
return _toPerson(response);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,13 @@ class SessionsAPIRepository extends ApiRepository {
|
||||
|
||||
Future<SessionCreateResponse> createSession(String deviceType, String deviceOS, {int? duration}) async {
|
||||
final dto = await checkNull(
|
||||
_api.createSession(SessionCreateDto(deviceType: deviceType, deviceOS: deviceOS, duration: duration)),
|
||||
_api.createSession(
|
||||
SessionCreateDto(
|
||||
deviceType: Optional.present(deviceType),
|
||||
deviceOS: Optional.present(deviceOS),
|
||||
duration: duration == null ? const Optional.absent() : Optional.present(duration),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return SessionCreateResponse(
|
||||
@@ -23,7 +29,7 @@ class SessionsAPIRepository extends ApiRepository {
|
||||
current: dto.current,
|
||||
deviceType: deviceType,
|
||||
deviceOS: deviceOS,
|
||||
expiresAt: dto.expiresAt,
|
||||
expiresAt: dto.expiresAt.orElse(null),
|
||||
createdAt: dto.createdAt,
|
||||
updatedAt: dto.updatedAt,
|
||||
token: dto.token,
|
||||
|
||||
@@ -55,7 +55,7 @@ class LockedGuard extends AutoRouteGuard {
|
||||
return;
|
||||
}
|
||||
|
||||
await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: securePinCode));
|
||||
await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: Optional.present(securePinCode)));
|
||||
|
||||
resolver.next(true);
|
||||
} on PlatformException catch (error) {
|
||||
|
||||
@@ -18,7 +18,11 @@ class OAuthService {
|
||||
log.info("Starting OAuth flow with redirect URI: $redirectUri");
|
||||
|
||||
final dto = await _apiService.oAuthApi.startOAuth(
|
||||
OAuthConfigDto(redirectUri: redirectUri, state: state, codeChallenge: codeChallenge),
|
||||
OAuthConfigDto(
|
||||
redirectUri: redirectUri,
|
||||
state: Optional.present(state),
|
||||
codeChallenge: Optional.present(codeChallenge),
|
||||
),
|
||||
);
|
||||
|
||||
final authUrl = dto?.url;
|
||||
@@ -37,7 +41,7 @@ class OAuthService {
|
||||
}
|
||||
|
||||
return await _apiService.oAuthApi.finishOAuth(
|
||||
OAuthCallbackDto(url: result, state: state, codeVerifier: codeVerifier),
|
||||
OAuthCallbackDto(url: result, state: Optional.present(state), codeVerifier: Optional.present(codeVerifier)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,26 +48,26 @@ class SharedLinkService {
|
||||
if (type == SharedLinkType.ALBUM) {
|
||||
dto = SharedLinkCreateDto(
|
||||
type: type,
|
||||
albumId: albumId,
|
||||
showMetadata: showMeta,
|
||||
allowDownload: allowDownload,
|
||||
allowUpload: allowUpload,
|
||||
expiresAt: expiresAt,
|
||||
description: description,
|
||||
password: password,
|
||||
slug: slug,
|
||||
albumId: albumId == null ? const Optional.absent() : Optional.present(albumId),
|
||||
showMetadata: Optional.present(showMeta),
|
||||
allowDownload: Optional.present(allowDownload),
|
||||
allowUpload: Optional.present(allowUpload),
|
||||
expiresAt: expiresAt == null ? const Optional.absent() : Optional.present(expiresAt),
|
||||
description: description == null ? const Optional.absent() : Optional.present(description),
|
||||
password: password == null ? const Optional.absent() : Optional.present(password),
|
||||
slug: slug == null ? const Optional.absent() : Optional.present(slug),
|
||||
);
|
||||
} else if (assetIds != null) {
|
||||
dto = SharedLinkCreateDto(
|
||||
type: type,
|
||||
showMetadata: showMeta,
|
||||
allowDownload: allowDownload,
|
||||
allowUpload: allowUpload,
|
||||
expiresAt: expiresAt,
|
||||
description: description,
|
||||
password: password,
|
||||
slug: slug,
|
||||
assetIds: assetIds,
|
||||
showMetadata: Optional.present(showMeta),
|
||||
allowDownload: Optional.present(allowDownload),
|
||||
allowUpload: Optional.present(allowUpload),
|
||||
expiresAt: expiresAt == null ? const Optional.absent() : Optional.present(expiresAt),
|
||||
description: description == null ? const Optional.absent() : Optional.present(description),
|
||||
password: password == null ? const Optional.absent() : Optional.present(password),
|
||||
slug: slug == null ? const Optional.absent() : Optional.present(slug),
|
||||
assetIds: Optional.present(assetIds),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -98,14 +98,14 @@ class SharedLinkService {
|
||||
final responseDto = await _apiService.sharedLinksApi.updateSharedLink(
|
||||
id,
|
||||
SharedLinkEditDto(
|
||||
showMetadata: showMeta,
|
||||
allowDownload: allowDownload,
|
||||
allowUpload: allowUpload,
|
||||
expiresAt: expiresAt,
|
||||
description: description,
|
||||
password: password,
|
||||
slug: slug,
|
||||
changeExpiryTime: changeExpiry,
|
||||
showMetadata: showMeta == null ? const Optional.absent() : Optional.present(showMeta),
|
||||
allowDownload: allowDownload == null ? const Optional.absent() : Optional.present(allowDownload),
|
||||
allowUpload: allowUpload == null ? const Optional.absent() : Optional.present(allowUpload),
|
||||
expiresAt: expiresAt == null ? const Optional.absent() : Optional.present(expiresAt),
|
||||
description: description == null ? const Optional.absent() : Optional.present(description),
|
||||
password: password == null ? const Optional.absent() : Optional.present(password),
|
||||
slug: slug == null ? const Optional.absent() : Optional.present(slug),
|
||||
changeExpiryTime: changeExpiry == null ? const Optional.absent() : Optional.present(changeExpiry),
|
||||
),
|
||||
);
|
||||
if (responseDto != null) {
|
||||
|
||||
@@ -8,10 +8,9 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:immich_mobile/utils/bootstrap.dart';
|
||||
import 'package:immich_mobile/utils/debug_print.dart';
|
||||
import 'package:immich_mobile/wm_executor.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:worker_manager/worker_manager.dart';
|
||||
import 'package:worker_manager/worker_manager.dart' show Cancelable;
|
||||
|
||||
class InvalidIsolateUsageException implements Exception {
|
||||
const InvalidIsolateUsageException();
|
||||
@@ -30,50 +29,27 @@ Cancelable<T?> runInIsolateGentle<T>({
|
||||
throw const InvalidIsolateUsageException();
|
||||
}
|
||||
|
||||
return workerManagerPatch.executeGentle((cancelledChecker) async {
|
||||
T? result;
|
||||
await runZonedGuarded(
|
||||
() async {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
return workerManagerPatch.executeGentle((onCancel) async {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
|
||||
final (drift, logDb) = await Bootstrap.initDomain(shouldBufferLogs: false, listenStoreUpdates: false);
|
||||
final ref = ProviderContainer(
|
||||
overrides: [
|
||||
cancellationProvider.overrideWithValue(cancelledChecker),
|
||||
driftProvider.overrideWith(driftOverride(drift)),
|
||||
],
|
||||
);
|
||||
|
||||
Logger log = Logger("IsolateLogger");
|
||||
|
||||
try {
|
||||
result = await computation(ref);
|
||||
} on CanceledError {
|
||||
log.warning("Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}");
|
||||
} catch (error, stack) {
|
||||
log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack);
|
||||
} finally {
|
||||
try {
|
||||
ref.dispose();
|
||||
|
||||
await Store.dispose();
|
||||
await LogService.I.dispose();
|
||||
await logDb.close();
|
||||
await drift.close();
|
||||
} catch (error, stack) {
|
||||
dPrint(() => "Error closing resources in isolate: $error, $stack");
|
||||
} finally {
|
||||
ref.dispose();
|
||||
// Delay to ensure all resources are released
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
}
|
||||
}
|
||||
},
|
||||
(error, stack) {
|
||||
dPrint(() => "Error in isolate $debugLabel zone: $error, $stack");
|
||||
},
|
||||
final log = Logger("IsolateLogger");
|
||||
final (drift, logDb) = await Bootstrap.initDomain(shouldBufferLogs: false, listenStoreUpdates: false);
|
||||
final ref = ProviderContainer(
|
||||
overrides: [cancellationProvider.overrideWithValue(onCancel), driftProvider.overrideWith(driftOverride(drift))],
|
||||
);
|
||||
return result;
|
||||
|
||||
try {
|
||||
return await computation(ref);
|
||||
} catch (error, stack) {
|
||||
log.severe("Error in runInIsolateGentle${debugLabel == null ? '' : ' for $debugLabel'}", error, stack);
|
||||
return null;
|
||||
} finally {
|
||||
ref.dispose();
|
||||
await Store.dispose();
|
||||
await LogService.I.dispose();
|
||||
await logDb.close();
|
||||
await drift.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
// Forked from worker_manager's `WorkerImpl` (src/worker/worker_io.dart): a
|
||||
// `CancelRequest` completes the computation's [Completer] (so it can await
|
||||
// cancellation and unwind) instead of flipping a polled flag, and [shutdown]
|
||||
// lets the isolate drain and exit on its own rather than force-killing it. Only
|
||||
// the gentle-with-cancellation path immich uses is kept.
|
||||
//
|
||||
// ignore_for_file: implementation_imports
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:worker_manager/src/scheduling/task.dart';
|
||||
import 'package:worker_manager/src/worker/cancel_request.dart';
|
||||
import 'package:worker_manager/src/worker/result.dart';
|
||||
|
||||
/// A worker computation that receives a [Completer] which completes on
|
||||
/// cancellation: await its future to react promptly, or read `isCompleted`.
|
||||
typedef GentleExecution<R> = FutureOr<R> Function(Completer<void> onCancel);
|
||||
|
||||
class _Shutdown {
|
||||
const _Shutdown();
|
||||
}
|
||||
|
||||
class IsolateWorker {
|
||||
IsolateWorker();
|
||||
|
||||
Isolate? _isolate;
|
||||
RawReceivePort? _receivePort;
|
||||
SendPort? _sendPort;
|
||||
Completer<void>? _sendPortReceived;
|
||||
Completer? _result;
|
||||
|
||||
String? taskId;
|
||||
|
||||
bool get initialized => _sendPortReceived?.isCompleted ?? false;
|
||||
|
||||
bool get initializing {
|
||||
final sendPortReceived = _sendPortReceived;
|
||||
return sendPortReceived != null && !sendPortReceived.isCompleted;
|
||||
}
|
||||
|
||||
Future<void> initialize() async {
|
||||
final sendPortReceived = _sendPortReceived = Completer<void>();
|
||||
final receivePort = _receivePort = RawReceivePort();
|
||||
receivePort.handler = (Object message) {
|
||||
if (message is SendPort) {
|
||||
_sendPort = message;
|
||||
sendPortReceived.complete();
|
||||
} else if (message is ResultSuccess) {
|
||||
_result?.complete(message.value);
|
||||
_afterTask();
|
||||
} else if (message is ResultError) {
|
||||
_result?.completeError(message.error, message.stackTrace);
|
||||
_afterTask();
|
||||
}
|
||||
};
|
||||
_isolate = await Isolate.spawn(_isolateEntry, receivePort.sendPort, errorsAreFatal: false);
|
||||
await sendPortReceived.future;
|
||||
}
|
||||
|
||||
Future<R> work<R>(Task<R> task) async {
|
||||
taskId = task.id;
|
||||
final result = _result = Completer();
|
||||
_sendPort!.send(task.execution);
|
||||
return await (result.future as Future<R>);
|
||||
}
|
||||
|
||||
/// Cancels the current task without retiring the worker.
|
||||
void cancelGentle() => _sendPort?.send(CancelRequest());
|
||||
|
||||
/// Cancels any in-flight task and awaits the isolate exiting on its own — no
|
||||
/// force-kill, so `finally` blocks and native cleanup always run.
|
||||
///
|
||||
/// Detaches the slot up front so a concurrent [initialize] can revive it
|
||||
/// without colliding (revival installs fresh ports while this drains the ones
|
||||
/// it captured locally). A revived worker is always idle, so the still-live
|
||||
/// receive-port handler can't misroute a result.
|
||||
Future<void> shutdown() async {
|
||||
final sendPortReceived = _sendPortReceived;
|
||||
if (sendPortReceived != null && !sendPortReceived.isCompleted) {
|
||||
await sendPortReceived.future;
|
||||
}
|
||||
|
||||
final isolate = _isolate;
|
||||
final receivePort = _receivePort;
|
||||
final sendPort = _sendPort;
|
||||
if (isolate == null || receivePort == null || sendPort == null) {
|
||||
return;
|
||||
}
|
||||
_isolate = null;
|
||||
_sendPort = null;
|
||||
_sendPortReceived = null;
|
||||
// Not _result: an in-flight task still delivers it before exiting; nulling
|
||||
// here would drop that and hang work()'s caller.
|
||||
|
||||
final exited = Completer<void>();
|
||||
final exitPort = RawReceivePort();
|
||||
exitPort.handler = (_) {
|
||||
if (!exited.isCompleted) {
|
||||
exited.complete();
|
||||
}
|
||||
exitPort.close();
|
||||
};
|
||||
isolate.addOnExitListener(exitPort.sendPort);
|
||||
sendPort.send(const _Shutdown());
|
||||
await exited.future;
|
||||
receivePort.close();
|
||||
}
|
||||
|
||||
void _afterTask() {
|
||||
taskId = null;
|
||||
_result = null;
|
||||
}
|
||||
|
||||
static void _isolateEntry(SendPort sendPort) {
|
||||
final receivePort = RawReceivePort();
|
||||
sendPort.send(receivePort.sendPort);
|
||||
// One task at a time, so a single completer suffices; null between tasks.
|
||||
Completer<void>? onCancel;
|
||||
void cancel() {
|
||||
if (onCancel?.isCompleted == false) {
|
||||
onCancel!.complete();
|
||||
}
|
||||
}
|
||||
|
||||
var shuttingDown = false;
|
||||
var running = false;
|
||||
receivePort.handler = (message) async {
|
||||
if (message is _Shutdown) {
|
||||
shuttingDown = true;
|
||||
cancel();
|
||||
if (!running) {
|
||||
Isolate.exit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (message is CancelRequest) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
final execution = message as GentleExecution;
|
||||
onCancel = Completer<void>();
|
||||
running = true;
|
||||
Result result;
|
||||
try {
|
||||
result = ResultSuccess(await execution(onCancel!));
|
||||
} catch (error, stackTrace) {
|
||||
result = ResultError(error, stackTrace);
|
||||
} finally {
|
||||
onCancel = null;
|
||||
running = false;
|
||||
}
|
||||
if (shuttingDown) {
|
||||
// An isolate that has used platform channels can't exit on its own (Flutter's BackgroundIsolateBinaryMessenger
|
||||
// opens an undisposable port), so closing our ports isn't enough. Isolate.exit delivers the result as its final
|
||||
// message and terminates. It's abrupt (skips pending finally/microtasks) but safe here: the computation and its
|
||||
// `finally` are already done and there's no await before this, so nothing pending is skipped.
|
||||
Isolate.exit(sendPort, result);
|
||||
}
|
||||
sendPort.send(result);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,69 +1,58 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
dynamic upgradeDto(dynamic value, String targetType) {
|
||||
switch (targetType) {
|
||||
case 'UserPreferencesResponseDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'download.includeEmbeddedVideos', false);
|
||||
addDefault(value, 'folders', FoldersResponse(enabled: false, sidebarWeb: false).toJson());
|
||||
addDefault(value, 'memories', MemoriesResponse(enabled: true, duration: 5).toJson());
|
||||
addDefault(value, 'ratings', RatingsResponse(enabled: false).toJson());
|
||||
addDefault(value, 'people', PeopleResponse(enabled: true, sidebarWeb: false).toJson());
|
||||
addDefault(value, 'tags', TagsResponse(enabled: false, sidebarWeb: false).toJson());
|
||||
addDefault(value, 'sharedLinks', SharedLinksResponse(enabled: true, sidebarWeb: false).toJson());
|
||||
addDefault(value, 'cast', CastResponse(gCastEnabled: false).toJson());
|
||||
addDefault(value, 'albums', {'defaultAssetOrder': 'desc'});
|
||||
}
|
||||
break;
|
||||
case 'ServerConfigDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'mapLightStyleUrl', 'https://tiles.immich.cloud/v1/style/light.json');
|
||||
addDefault(value, 'mapDarkStyleUrl', 'https://tiles.immich.cloud/v1/style/dark.json');
|
||||
addDefault(value, 'minFaces', 3);
|
||||
}
|
||||
case 'UserResponseDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
|
||||
}
|
||||
break;
|
||||
case 'AssetResponseDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'visibility', 'timeline');
|
||||
addDefault(value, 'createdAt', DateTime.now().toIso8601String());
|
||||
addDefault(value, 'isEdited', false);
|
||||
}
|
||||
break;
|
||||
case 'UserAdminResponseDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
|
||||
}
|
||||
break;
|
||||
case 'LoginResponseDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'isOnboarded', false);
|
||||
}
|
||||
break;
|
||||
case 'SyncUserV1':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
|
||||
addDefault(value, 'hasProfileImage', false);
|
||||
}
|
||||
case 'SyncAssetV1':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'isEdited', false);
|
||||
}
|
||||
case 'ServerFeaturesDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'ocr', false);
|
||||
addDefault(value, 'realtimeTranscoding', false);
|
||||
}
|
||||
break;
|
||||
case 'MemoriesResponse':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'duration', 5);
|
||||
}
|
||||
break;
|
||||
abstract interface class _Dynamic {
|
||||
Object? resolve();
|
||||
}
|
||||
|
||||
class _CurrentTimestamp implements _Dynamic {
|
||||
const _CurrentTimestamp();
|
||||
|
||||
@override
|
||||
Object? resolve() => DateTime.now().toIso8601String();
|
||||
}
|
||||
|
||||
const _now = _CurrentTimestamp();
|
||||
|
||||
@visibleForTesting
|
||||
final Map<String, Map<String, Object?>> openApiPatches = {
|
||||
'UserPreferencesResponseDto': {
|
||||
'download.includeEmbeddedVideos': false,
|
||||
'folders': FoldersResponse(enabled: false, sidebarWeb: false).toJson(),
|
||||
'memories': MemoriesResponse(enabled: true, duration: 5).toJson(),
|
||||
'ratings': RatingsResponse(enabled: false).toJson(),
|
||||
'people': PeopleResponse(enabled: true, sidebarWeb: false).toJson(),
|
||||
'tags': TagsResponse(enabled: false, sidebarWeb: false).toJson(),
|
||||
'sharedLinks': SharedLinksResponse(enabled: true, sidebarWeb: false).toJson(),
|
||||
'cast': CastResponse(gCastEnabled: false).toJson(),
|
||||
'albums': {'defaultAssetOrder': 'desc'},
|
||||
},
|
||||
'ServerConfigDto': {
|
||||
'mapLightStyleUrl': 'https://tiles.immich.cloud/v1/style/light.json',
|
||||
'mapDarkStyleUrl': 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||
'minFaces': 3,
|
||||
},
|
||||
'UserResponseDto': {'profileChangedAt': _now},
|
||||
'AssetResponseDto': {'visibility': 'timeline', 'createdAt': _now, 'isEdited': false},
|
||||
'UserAdminResponseDto': {'profileChangedAt': _now},
|
||||
'LoginResponseDto': {'isOnboarded': false},
|
||||
'SyncUserV1': {'profileChangedAt': _now, 'hasProfileImage': false},
|
||||
'SyncAssetV1': {'isEdited': false},
|
||||
'ServerFeaturesDto': {'ocr': false, 'realtimeTranscoding': false},
|
||||
'MemoriesResponse': {'duration': 5},
|
||||
};
|
||||
|
||||
void upgradeDto(dynamic value, String targetType) {
|
||||
if (value is! Map) {
|
||||
return;
|
||||
}
|
||||
final fields = openApiPatches[targetType];
|
||||
if (fields == null) {
|
||||
return;
|
||||
}
|
||||
fields.forEach((key, defaultValue) {
|
||||
addDefault(value, key, defaultValue is _Dynamic ? defaultValue.resolve() : defaultValue);
|
||||
});
|
||||
}
|
||||
|
||||
addDefault(dynamic value, String keys, dynamic defaultValue) {
|
||||
|
||||
+31
-109
@@ -6,8 +6,8 @@ import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:immich_mobile/utils/isolate_worker.dart';
|
||||
import 'package:worker_manager/src/number_of_processors/processors_io.dart';
|
||||
import 'package:worker_manager/src/worker/worker.dart';
|
||||
import 'package:worker_manager/worker_manager.dart';
|
||||
|
||||
final workerManagerPatch = _Executor();
|
||||
@@ -16,6 +16,13 @@ final workerManagerPatch = _Executor();
|
||||
const _minId = -9007199254740992;
|
||||
const _maxId = 9007199254740992;
|
||||
|
||||
class _GentleTask<R> extends Task<R> implements Gentle {
|
||||
@override
|
||||
final GentleExecution<R> execution;
|
||||
|
||||
_GentleTask({required super.id, required super.completer, required super.workPriority, required this.execution});
|
||||
}
|
||||
|
||||
class Mixinable<T> {
|
||||
late final itSelf = this as T;
|
||||
}
|
||||
@@ -51,13 +58,13 @@ mixin _ExecutorLogger on Mixinable<_Executor> {
|
||||
|
||||
class _Executor extends Mixinable<_Executor> with _ExecutorLogger {
|
||||
final _queue = PriorityQueue<Task>();
|
||||
final _pool = <Worker>[];
|
||||
final _pool = <IsolateWorker>[];
|
||||
var _nextTaskId = _minId;
|
||||
var _dynamicSpawning = false;
|
||||
var _isolatesCount = numberOfProcessors;
|
||||
|
||||
@visibleForTesting
|
||||
UnmodifiableListView<Worker> get pool => UnmodifiableListView(_pool);
|
||||
UnmodifiableListView<IsolateWorker> get pool => UnmodifiableListView(_pool);
|
||||
|
||||
@override
|
||||
Future<void> init({int? isolatesCount, bool? dynamicSpawning}) async {
|
||||
@@ -80,117 +87,37 @@ class _Executor extends Mixinable<_Executor> with _ExecutorLogger {
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
_queue.clear();
|
||||
for (final worker in _pool) {
|
||||
if (worker.initialized || worker.initializing) {
|
||||
worker.kill();
|
||||
}
|
||||
}
|
||||
final shutdown = _pool.map((worker) => worker.shutdown()).toList(growable: false);
|
||||
_pool.clear();
|
||||
await Future.wait(shutdown);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Cancelable<R> execute<R>(Execute<R> execution, {WorkPriority priority = WorkPriority.immediately}) {
|
||||
return _createCancelable<R>(execution: execution, priority: priority);
|
||||
}
|
||||
|
||||
Cancelable<R> executeNow<R>(ExecuteGentle<R> execution) {
|
||||
final task = TaskGentle<R>(
|
||||
id: "",
|
||||
workPriority: WorkPriority.immediately,
|
||||
execution: execution,
|
||||
completer: Completer<R>(),
|
||||
);
|
||||
|
||||
Future<void> run() async {
|
||||
try {
|
||||
final result = await execution(() => task.canceled);
|
||||
task.complete(result, null, null);
|
||||
} catch (error, st) {
|
||||
task.complete(null, error, st);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
return Cancelable(completer: task.completer, onCancel: () => _cancel(task));
|
||||
}
|
||||
|
||||
Cancelable<R> executeWithPort<R, T>(
|
||||
ExecuteWithPort<R> execution, {
|
||||
WorkPriority priority = WorkPriority.immediately,
|
||||
required void Function(T value) onMessage,
|
||||
}) {
|
||||
return _createCancelable<R>(
|
||||
execution: execution,
|
||||
priority: priority,
|
||||
onMessage: (message) => onMessage(message as T),
|
||||
);
|
||||
}
|
||||
|
||||
Cancelable<R> executeGentle<R>(ExecuteGentle<R> execution, {WorkPriority priority = WorkPriority.immediately}) {
|
||||
return _createCancelable<R>(execution: execution, priority: priority);
|
||||
}
|
||||
|
||||
Cancelable<R> executeGentleWithPort<R, T>(
|
||||
ExecuteGentleWithPort<R> execution, {
|
||||
WorkPriority priority = WorkPriority.immediately,
|
||||
required void Function(T value) onMessage,
|
||||
}) {
|
||||
return _createCancelable<R>(
|
||||
execution: execution,
|
||||
priority: priority,
|
||||
onMessage: (message) => onMessage(message as T),
|
||||
);
|
||||
}
|
||||
|
||||
void _createWorkers() {
|
||||
for (var i = 0; i < _isolatesCount; i++) {
|
||||
_pool.add(Worker());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializeWorkers() async {
|
||||
await Future.wait(_pool.map((e) => e.initialize()));
|
||||
}
|
||||
|
||||
Cancelable<R> _createCancelable<R>({
|
||||
required Function execution,
|
||||
WorkPriority priority = WorkPriority.immediately,
|
||||
void Function(Object value)? onMessage,
|
||||
}) {
|
||||
/// Runs [execution] on a worker isolate; its [Completer] completes when the
|
||||
/// returned [Cancelable] is cancelled.
|
||||
Cancelable<R> executeGentle<R>(GentleExecution<R> execution, {WorkPriority priority = WorkPriority.immediately}) {
|
||||
if (_nextTaskId + 1 == _maxId) {
|
||||
_nextTaskId = _minId;
|
||||
}
|
||||
final id = _nextTaskId.toString();
|
||||
_nextTaskId++;
|
||||
late final Task<R> task;
|
||||
final completer = Completer<R>();
|
||||
if (execution is ExecuteWithPort<R>) {
|
||||
task = TaskWithPort<R>(
|
||||
id: id,
|
||||
workPriority: priority,
|
||||
execution: execution,
|
||||
completer: completer,
|
||||
onMessage: onMessage!,
|
||||
);
|
||||
} else if (execution is ExecuteGentle<R>) {
|
||||
task = TaskGentle<R>(id: id, workPriority: priority, execution: execution, completer: completer);
|
||||
} else if (execution is ExecuteGentleWithPort<R>) {
|
||||
task = TaskGentleWithPort<R>(
|
||||
id: id,
|
||||
workPriority: priority,
|
||||
execution: execution,
|
||||
completer: completer,
|
||||
onMessage: onMessage!,
|
||||
);
|
||||
} else if (execution is Execute<R>) {
|
||||
task = TaskRegular<R>(id: id, workPriority: priority, execution: execution, completer: completer);
|
||||
}
|
||||
final task = _GentleTask<R>(id: id, workPriority: priority, execution: execution, completer: Completer<R>());
|
||||
_queue.add(task);
|
||||
_schedule();
|
||||
logTaskAdded(task.id);
|
||||
return Cancelable(completer: task.completer, onCancel: () => _cancel(task));
|
||||
}
|
||||
|
||||
void _createWorkers() {
|
||||
for (var i = 0; i < _isolatesCount; i++) {
|
||||
_pool.add(IsolateWorker());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializeWorkers() async {
|
||||
await Future.wait(_pool.map((e) => e.initialize()));
|
||||
}
|
||||
|
||||
Future<void> _ensureWorkersInitialized() async {
|
||||
if (_pool.isEmpty) {
|
||||
_createWorkers();
|
||||
@@ -240,7 +167,9 @@ class _Executor extends Mixinable<_Executor> with _ExecutorLogger {
|
||||
)
|
||||
.whenComplete(() {
|
||||
if (_dynamicSpawning && _queue.isEmpty) {
|
||||
availableWorker.kill();
|
||||
// Retire the idle worker; shutdown() nulls its fields so the husk
|
||||
// stays pooled and is revived by initialize() if work arrives.
|
||||
unawaited(availableWorker.shutdown());
|
||||
}
|
||||
_schedule();
|
||||
});
|
||||
@@ -250,15 +179,8 @@ class _Executor extends Mixinable<_Executor> with _ExecutorLogger {
|
||||
void _cancel(Task task) {
|
||||
task.cancel();
|
||||
_queue.remove(task);
|
||||
final targetWorker = _pool.firstWhereOrNull((worker) => worker.taskId == task.id);
|
||||
if (task is Gentle) {
|
||||
targetWorker?.cancelGentle();
|
||||
} else {
|
||||
targetWorker?.kill();
|
||||
if (!_dynamicSpawning) {
|
||||
targetWorker?.initialize();
|
||||
}
|
||||
}
|
||||
// All tasks are gentle: signal cancellation; the worker unwinds on its own.
|
||||
_pool.firstWhereOrNull((worker) => worker.taskId == task.id)?.cancelGentle();
|
||||
super._cancel(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
7.8.0
|
||||
7.22.0
|
||||
|
||||
Generated
+1
-1
@@ -4,7 +4,7 @@ Immich API
|
||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||
|
||||
- API version: 3.0.0
|
||||
- Generator version: 7.8.0
|
||||
- Generator version: 7.22.0
|
||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||
|
||||
## Requirements
|
||||
|
||||
Generated
+1
@@ -29,6 +29,7 @@ part 'auth/api_key_auth.dart';
|
||||
part 'auth/oauth.dart';
|
||||
part 'auth/http_basic_auth.dart';
|
||||
part 'auth/http_bearer_auth.dart';
|
||||
part 'optional.dart';
|
||||
|
||||
part 'api/api_keys_api.dart';
|
||||
part 'api/activities_api.dart';
|
||||
|
||||
Generated
+3
@@ -226,6 +226,9 @@ Future<String> _decodeBodyBytes(Response response) async {
|
||||
/// Returns a valid [T] value found at the specified Map [key], null otherwise.
|
||||
T? mapValueOfType<T>(dynamic map, String key) {
|
||||
final dynamic value = map is Map ? map[key] : null;
|
||||
if (T == double && value is int) {
|
||||
return value.toDouble() as T;
|
||||
}
|
||||
return value is T ? value : null;
|
||||
}
|
||||
|
||||
|
||||
+12
-14
@@ -14,8 +14,8 @@ class ActivityCreateDto {
|
||||
/// Returns a new [ActivityCreateDto] instance.
|
||||
ActivityCreateDto({
|
||||
required this.albumId,
|
||||
this.assetId,
|
||||
this.comment,
|
||||
this.assetId = const Optional.absent(),
|
||||
this.comment = const Optional.absent(),
|
||||
required this.type,
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ class ActivityCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? assetId;
|
||||
Optional<String?> assetId;
|
||||
|
||||
/// Comment text (required if type is comment)
|
||||
///
|
||||
@@ -38,7 +38,7 @@ class ActivityCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? comment;
|
||||
Optional<String?> comment;
|
||||
|
||||
ReactionType type;
|
||||
|
||||
@@ -63,15 +63,13 @@ class ActivityCreateDto {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'albumId'] = this.albumId;
|
||||
if (this.assetId != null) {
|
||||
json[r'assetId'] = this.assetId;
|
||||
} else {
|
||||
// json[r'assetId'] = null;
|
||||
if (this.assetId.isPresent) {
|
||||
final value = this.assetId.value;
|
||||
json[r'assetId'] = value;
|
||||
}
|
||||
if (this.comment != null) {
|
||||
json[r'comment'] = this.comment;
|
||||
} else {
|
||||
// json[r'comment'] = null;
|
||||
if (this.comment.isPresent) {
|
||||
final value = this.comment.value;
|
||||
json[r'comment'] = value;
|
||||
}
|
||||
json[r'type'] = this.type;
|
||||
return json;
|
||||
@@ -87,8 +85,8 @@ class ActivityCreateDto {
|
||||
|
||||
return ActivityCreateDto(
|
||||
albumId: mapValueOfType<String>(json, r'albumId')!,
|
||||
assetId: mapValueOfType<String>(json, r'assetId'),
|
||||
comment: mapValueOfType<String>(json, r'comment'),
|
||||
assetId: json.containsKey(r'assetId') ? Optional.present(mapValueOfType<String>(json, r'assetId')) : const Optional.absent(),
|
||||
comment: json.containsKey(r'comment') ? Optional.present(mapValueOfType<String>(json, r'comment')) : const Optional.absent(),
|
||||
type: ReactionType.fromJson(json[r'type'])!,
|
||||
);
|
||||
}
|
||||
|
||||
+7
-8
@@ -14,7 +14,7 @@ class ActivityResponseDto {
|
||||
/// Returns a new [ActivityResponseDto] instance.
|
||||
ActivityResponseDto({
|
||||
required this.assetId,
|
||||
this.comment,
|
||||
this.comment = const Optional.absent(),
|
||||
required this.createdAt,
|
||||
required this.id,
|
||||
required this.type,
|
||||
@@ -25,7 +25,7 @@ class ActivityResponseDto {
|
||||
String? assetId;
|
||||
|
||||
/// Comment text (for comment activities)
|
||||
String? comment;
|
||||
Optional<String?> comment;
|
||||
|
||||
/// Creation date
|
||||
DateTime createdAt;
|
||||
@@ -64,12 +64,11 @@ class ActivityResponseDto {
|
||||
if (this.assetId != null) {
|
||||
json[r'assetId'] = this.assetId;
|
||||
} else {
|
||||
// json[r'assetId'] = null;
|
||||
json[r'assetId'] = null;
|
||||
}
|
||||
if (this.comment != null) {
|
||||
json[r'comment'] = this.comment;
|
||||
} else {
|
||||
// json[r'comment'] = null;
|
||||
if (this.comment.isPresent) {
|
||||
final value = this.comment.value;
|
||||
json[r'comment'] = value;
|
||||
}
|
||||
json[r'createdAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.createdAt.millisecondsSinceEpoch
|
||||
@@ -90,7 +89,7 @@ class ActivityResponseDto {
|
||||
|
||||
return ActivityResponseDto(
|
||||
assetId: mapValueOfType<String>(json, r'assetId'),
|
||||
comment: mapValueOfType<String>(json, r'comment'),
|
||||
comment: json.containsKey(r'comment') ? Optional.present(mapValueOfType<String>(json, r'comment')) : const Optional.absent(),
|
||||
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
type: ReactionType.fromJson(json[r'type'])!,
|
||||
|
||||
+32
-33
@@ -17,17 +17,17 @@ class AlbumResponseDto {
|
||||
required this.albumThumbnailAssetId,
|
||||
this.albumUsers = const [],
|
||||
required this.assetCount,
|
||||
this.contributorCounts = const [],
|
||||
this.contributorCounts = const Optional.present(const []),
|
||||
required this.createdAt,
|
||||
required this.description,
|
||||
this.endDate,
|
||||
this.endDate = const Optional.absent(),
|
||||
required this.hasSharedLink,
|
||||
required this.id,
|
||||
required this.isActivityEnabled,
|
||||
this.lastModifiedAssetTimestamp,
|
||||
this.order,
|
||||
this.lastModifiedAssetTimestamp = const Optional.absent(),
|
||||
this.order = const Optional.absent(),
|
||||
required this.shared,
|
||||
this.startDate,
|
||||
this.startDate = const Optional.absent(),
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ class AlbumResponseDto {
|
||||
/// Maximum value: 9007199254740991
|
||||
int assetCount;
|
||||
|
||||
List<ContributorCountResponseDto> contributorCounts;
|
||||
Optional<List<ContributorCountResponseDto>?> contributorCounts;
|
||||
|
||||
/// Creation date
|
||||
DateTime createdAt;
|
||||
@@ -61,7 +61,7 @@ class AlbumResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? endDate;
|
||||
Optional<DateTime?> endDate;
|
||||
|
||||
/// Has shared link
|
||||
bool hasSharedLink;
|
||||
@@ -79,7 +79,7 @@ class AlbumResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? lastModifiedAssetTimestamp;
|
||||
Optional<DateTime?> lastModifiedAssetTimestamp;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -87,7 +87,7 @@ class AlbumResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetOrder? order;
|
||||
Optional<AssetOrder?> order;
|
||||
|
||||
/// Is shared album
|
||||
bool shared;
|
||||
@@ -99,7 +99,7 @@ class AlbumResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? startDate;
|
||||
Optional<DateTime?> startDate;
|
||||
|
||||
/// Last update date
|
||||
DateTime updatedAt;
|
||||
@@ -152,36 +152,35 @@ class AlbumResponseDto {
|
||||
if (this.albumThumbnailAssetId != null) {
|
||||
json[r'albumThumbnailAssetId'] = this.albumThumbnailAssetId;
|
||||
} else {
|
||||
// json[r'albumThumbnailAssetId'] = null;
|
||||
json[r'albumThumbnailAssetId'] = null;
|
||||
}
|
||||
json[r'albumUsers'] = this.albumUsers;
|
||||
json[r'assetCount'] = this.assetCount;
|
||||
json[r'contributorCounts'] = this.contributorCounts;
|
||||
if (this.contributorCounts.isPresent) {
|
||||
final value = this.contributorCounts.value;
|
||||
json[r'contributorCounts'] = value;
|
||||
}
|
||||
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
|
||||
json[r'description'] = this.description;
|
||||
if (this.endDate != null) {
|
||||
json[r'endDate'] = this.endDate!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'endDate'] = null;
|
||||
if (this.endDate.isPresent) {
|
||||
final value = this.endDate.value;
|
||||
json[r'endDate'] = value == null ? null : value.toUtc().toIso8601String();
|
||||
}
|
||||
json[r'hasSharedLink'] = this.hasSharedLink;
|
||||
json[r'id'] = this.id;
|
||||
json[r'isActivityEnabled'] = this.isActivityEnabled;
|
||||
if (this.lastModifiedAssetTimestamp != null) {
|
||||
json[r'lastModifiedAssetTimestamp'] = this.lastModifiedAssetTimestamp!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'lastModifiedAssetTimestamp'] = null;
|
||||
if (this.lastModifiedAssetTimestamp.isPresent) {
|
||||
final value = this.lastModifiedAssetTimestamp.value;
|
||||
json[r'lastModifiedAssetTimestamp'] = value == null ? null : value.toUtc().toIso8601String();
|
||||
}
|
||||
if (this.order != null) {
|
||||
json[r'order'] = this.order;
|
||||
} else {
|
||||
// json[r'order'] = null;
|
||||
if (this.order.isPresent) {
|
||||
final value = this.order.value;
|
||||
json[r'order'] = value;
|
||||
}
|
||||
json[r'shared'] = this.shared;
|
||||
if (this.startDate != null) {
|
||||
json[r'startDate'] = this.startDate!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'startDate'] = null;
|
||||
if (this.startDate.isPresent) {
|
||||
final value = this.startDate.value;
|
||||
json[r'startDate'] = value == null ? null : value.toUtc().toIso8601String();
|
||||
}
|
||||
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
|
||||
return json;
|
||||
@@ -200,17 +199,17 @@ class AlbumResponseDto {
|
||||
albumThumbnailAssetId: mapValueOfType<String>(json, r'albumThumbnailAssetId'),
|
||||
albumUsers: AlbumUserResponseDto.listFromJson(json[r'albumUsers']),
|
||||
assetCount: mapValueOfType<int>(json, r'assetCount')!,
|
||||
contributorCounts: ContributorCountResponseDto.listFromJson(json[r'contributorCounts']),
|
||||
contributorCounts: json.containsKey(r'contributorCounts') ? Optional.present(ContributorCountResponseDto.listFromJson(json[r'contributorCounts'])) : const Optional.absent(),
|
||||
createdAt: mapDateTime(json, r'createdAt', r'')!,
|
||||
description: mapValueOfType<String>(json, r'description')!,
|
||||
endDate: mapDateTime(json, r'endDate', r''),
|
||||
endDate: json.containsKey(r'endDate') ? Optional.present(mapDateTime(json, r'endDate', r'')) : const Optional.absent(),
|
||||
hasSharedLink: mapValueOfType<bool>(json, r'hasSharedLink')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isActivityEnabled: mapValueOfType<bool>(json, r'isActivityEnabled')!,
|
||||
lastModifiedAssetTimestamp: mapDateTime(json, r'lastModifiedAssetTimestamp', r''),
|
||||
order: AssetOrder.fromJson(json[r'order']),
|
||||
lastModifiedAssetTimestamp: json.containsKey(r'lastModifiedAssetTimestamp') ? Optional.present(mapDateTime(json, r'lastModifiedAssetTimestamp', r'')) : const Optional.absent(),
|
||||
order: json.containsKey(r'order') ? Optional.present(AssetOrder.fromJson(json[r'order'])) : const Optional.absent(),
|
||||
shared: mapValueOfType<bool>(json, r'shared')!,
|
||||
startDate: mapDateTime(json, r'startDate', r''),
|
||||
startDate: json.containsKey(r'startDate') ? Optional.present(mapDateTime(json, r'startDate', r'')) : const Optional.absent(),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
|
||||
);
|
||||
}
|
||||
|
||||
+6
-7
@@ -13,7 +13,7 @@ part of openapi.api;
|
||||
class AlbumUserAddDto {
|
||||
/// Returns a new [AlbumUserAddDto] instance.
|
||||
AlbumUserAddDto({
|
||||
this.role,
|
||||
this.role = const Optional.absent(),
|
||||
required this.userId,
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ class AlbumUserAddDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AlbumUserRole? role;
|
||||
Optional<AlbumUserRole?> role;
|
||||
|
||||
/// User ID
|
||||
String userId;
|
||||
@@ -44,10 +44,9 @@ class AlbumUserAddDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.role != null) {
|
||||
json[r'role'] = this.role;
|
||||
} else {
|
||||
// json[r'role'] = null;
|
||||
if (this.role.isPresent) {
|
||||
final value = this.role.value;
|
||||
json[r'role'] = value;
|
||||
}
|
||||
json[r'userId'] = this.userId;
|
||||
return json;
|
||||
@@ -62,7 +61,7 @@ class AlbumUserAddDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AlbumUserAddDto(
|
||||
role: AlbumUserRole.fromJson(json[r'role']),
|
||||
role: json.containsKey(r'role') ? Optional.present(AlbumUserRole.fromJson(json[r'role'])) : const Optional.absent(),
|
||||
userId: mapValueOfType<String>(json, r'userId')!,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ part of openapi.api;
|
||||
class AlbumsAddAssetsResponseDto {
|
||||
/// Returns a new [AlbumsAddAssetsResponseDto] instance.
|
||||
AlbumsAddAssetsResponseDto({
|
||||
this.error,
|
||||
this.error = const Optional.absent(),
|
||||
required this.success,
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ class AlbumsAddAssetsResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
BulkIdErrorReason? error;
|
||||
Optional<BulkIdErrorReason?> error;
|
||||
|
||||
/// Operation success
|
||||
bool success;
|
||||
@@ -44,10 +44,9 @@ class AlbumsAddAssetsResponseDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.error != null) {
|
||||
json[r'error'] = this.error;
|
||||
} else {
|
||||
// json[r'error'] = null;
|
||||
if (this.error.isPresent) {
|
||||
final value = this.error.value;
|
||||
json[r'error'] = value;
|
||||
}
|
||||
json[r'success'] = this.success;
|
||||
return json;
|
||||
@@ -62,7 +61,7 @@ class AlbumsAddAssetsResponseDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AlbumsAddAssetsResponseDto(
|
||||
error: BulkIdErrorReason.fromJson(json[r'error']),
|
||||
error: json.containsKey(r'error') ? Optional.present(BulkIdErrorReason.fromJson(json[r'error'])) : const Optional.absent(),
|
||||
success: mapValueOfType<bool>(json, r'success')!,
|
||||
);
|
||||
}
|
||||
|
||||
+6
-7
@@ -13,7 +13,7 @@ part of openapi.api;
|
||||
class AlbumsUpdate {
|
||||
/// Returns a new [AlbumsUpdate] instance.
|
||||
AlbumsUpdate({
|
||||
this.defaultAssetOrder,
|
||||
this.defaultAssetOrder = const Optional.absent(),
|
||||
});
|
||||
|
||||
///
|
||||
@@ -22,7 +22,7 @@ class AlbumsUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetOrder? defaultAssetOrder;
|
||||
Optional<AssetOrder?> defaultAssetOrder;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AlbumsUpdate &&
|
||||
@@ -38,10 +38,9 @@ class AlbumsUpdate {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.defaultAssetOrder != null) {
|
||||
json[r'defaultAssetOrder'] = this.defaultAssetOrder;
|
||||
} else {
|
||||
// json[r'defaultAssetOrder'] = null;
|
||||
if (this.defaultAssetOrder.isPresent) {
|
||||
final value = this.defaultAssetOrder.value;
|
||||
json[r'defaultAssetOrder'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -55,7 +54,7 @@ class AlbumsUpdate {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AlbumsUpdate(
|
||||
defaultAssetOrder: AssetOrder.fromJson(json[r'defaultAssetOrder']),
|
||||
defaultAssetOrder: json.containsKey(r'defaultAssetOrder') ? Optional.present(AssetOrder.fromJson(json[r'defaultAssetOrder'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+6
-7
@@ -13,7 +13,7 @@ part of openapi.api;
|
||||
class ApiKeyCreateDto {
|
||||
/// Returns a new [ApiKeyCreateDto] instance.
|
||||
ApiKeyCreateDto({
|
||||
this.name,
|
||||
this.name = const Optional.absent(),
|
||||
this.permissions = const [],
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ class ApiKeyCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? name;
|
||||
Optional<String?> name;
|
||||
|
||||
/// List of permissions
|
||||
List<Permission> permissions;
|
||||
@@ -45,10 +45,9 @@ class ApiKeyCreateDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.name != null) {
|
||||
json[r'name'] = this.name;
|
||||
} else {
|
||||
// json[r'name'] = null;
|
||||
if (this.name.isPresent) {
|
||||
final value = this.name.value;
|
||||
json[r'name'] = value;
|
||||
}
|
||||
json[r'permissions'] = this.permissions;
|
||||
return json;
|
||||
@@ -63,7 +62,7 @@ class ApiKeyCreateDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return ApiKeyCreateDto(
|
||||
name: mapValueOfType<String>(json, r'name'),
|
||||
name: json.containsKey(r'name') ? Optional.present(mapValueOfType<String>(json, r'name')) : const Optional.absent(),
|
||||
permissions: Permission.listFromJson(json[r'permissions']),
|
||||
);
|
||||
}
|
||||
|
||||
+13
-11
@@ -13,8 +13,8 @@ part of openapi.api;
|
||||
class ApiKeyUpdateDto {
|
||||
/// Returns a new [ApiKeyUpdateDto] instance.
|
||||
ApiKeyUpdateDto({
|
||||
this.name,
|
||||
this.permissions = const [],
|
||||
this.name = const Optional.absent(),
|
||||
this.permissions = const Optional.present(const []),
|
||||
});
|
||||
|
||||
/// API key name
|
||||
@@ -24,10 +24,10 @@ class ApiKeyUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? name;
|
||||
Optional<String?> name;
|
||||
|
||||
/// List of permissions
|
||||
List<Permission> permissions;
|
||||
Optional<List<Permission>?> permissions;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is ApiKeyUpdateDto &&
|
||||
@@ -45,12 +45,14 @@ class ApiKeyUpdateDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.name != null) {
|
||||
json[r'name'] = this.name;
|
||||
} else {
|
||||
// json[r'name'] = null;
|
||||
if (this.name.isPresent) {
|
||||
final value = this.name.value;
|
||||
json[r'name'] = value;
|
||||
}
|
||||
if (this.permissions.isPresent) {
|
||||
final value = this.permissions.value;
|
||||
json[r'permissions'] = value;
|
||||
}
|
||||
json[r'permissions'] = this.permissions;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -63,8 +65,8 @@ class ApiKeyUpdateDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return ApiKeyUpdateDto(
|
||||
name: mapValueOfType<String>(json, r'name'),
|
||||
permissions: Permission.listFromJson(json[r'permissions']),
|
||||
name: json.containsKey(r'name') ? Optional.present(mapValueOfType<String>(json, r'name')) : const Optional.absent(),
|
||||
permissions: json.containsKey(r'permissions') ? Optional.present(Permission.listFromJson(json[r'permissions'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+6
-7
@@ -13,7 +13,7 @@ part of openapi.api;
|
||||
class AssetBulkDeleteDto {
|
||||
/// Returns a new [AssetBulkDeleteDto] instance.
|
||||
AssetBulkDeleteDto({
|
||||
this.force,
|
||||
this.force = const Optional.absent(),
|
||||
this.ids = const [],
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ class AssetBulkDeleteDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? force;
|
||||
Optional<bool?> force;
|
||||
|
||||
/// IDs to process
|
||||
List<String> ids;
|
||||
@@ -45,10 +45,9 @@ class AssetBulkDeleteDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.force != null) {
|
||||
json[r'force'] = this.force;
|
||||
} else {
|
||||
// json[r'force'] = null;
|
||||
if (this.force.isPresent) {
|
||||
final value = this.force.value;
|
||||
json[r'force'] = value;
|
||||
}
|
||||
json[r'ids'] = this.ids;
|
||||
return json;
|
||||
@@ -63,7 +62,7 @@ class AssetBulkDeleteDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AssetBulkDeleteDto(
|
||||
force: mapValueOfType<bool>(json, r'force'),
|
||||
force: json.containsKey(r'force') ? Optional.present(mapValueOfType<bool>(json, r'force')) : const Optional.absent(),
|
||||
ids: json[r'ids'] is Iterable
|
||||
? (json[r'ids'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
|
||||
+60
-70
@@ -13,17 +13,17 @@ part of openapi.api;
|
||||
class AssetBulkUpdateDto {
|
||||
/// Returns a new [AssetBulkUpdateDto] instance.
|
||||
AssetBulkUpdateDto({
|
||||
this.dateTimeOriginal,
|
||||
this.dateTimeRelative,
|
||||
this.description,
|
||||
this.duplicateId,
|
||||
this.dateTimeOriginal = const Optional.absent(),
|
||||
this.dateTimeRelative = const Optional.absent(),
|
||||
this.description = const Optional.absent(),
|
||||
this.duplicateId = const Optional.absent(),
|
||||
this.ids = const [],
|
||||
this.isFavorite,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.rating,
|
||||
this.timeZone,
|
||||
this.visibility,
|
||||
this.isFavorite = const Optional.absent(),
|
||||
this.latitude = const Optional.absent(),
|
||||
this.longitude = const Optional.absent(),
|
||||
this.rating = const Optional.absent(),
|
||||
this.timeZone = const Optional.absent(),
|
||||
this.visibility = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Original date and time
|
||||
@@ -33,7 +33,7 @@ class AssetBulkUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? dateTimeOriginal;
|
||||
Optional<String?> dateTimeOriginal;
|
||||
|
||||
/// Relative time offset in seconds
|
||||
///
|
||||
@@ -45,7 +45,7 @@ class AssetBulkUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? dateTimeRelative;
|
||||
Optional<int?> dateTimeRelative;
|
||||
|
||||
/// Asset description
|
||||
///
|
||||
@@ -54,10 +54,10 @@ class AssetBulkUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? description;
|
||||
Optional<String?> description;
|
||||
|
||||
/// Duplicate ID
|
||||
String? duplicateId;
|
||||
Optional<String?> duplicateId;
|
||||
|
||||
/// Asset IDs to update
|
||||
List<String> ids;
|
||||
@@ -69,7 +69,7 @@ class AssetBulkUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isFavorite;
|
||||
Optional<bool?> isFavorite;
|
||||
|
||||
/// Latitude coordinate
|
||||
///
|
||||
@@ -81,7 +81,7 @@ class AssetBulkUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? latitude;
|
||||
Optional<num?> latitude;
|
||||
|
||||
/// Longitude coordinate
|
||||
///
|
||||
@@ -93,13 +93,13 @@ class AssetBulkUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? longitude;
|
||||
Optional<num?> longitude;
|
||||
|
||||
/// Rating in range [1-5], or null for unrated
|
||||
///
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
int? rating;
|
||||
Optional<int?> rating;
|
||||
|
||||
/// Time zone (IANA timezone)
|
||||
///
|
||||
@@ -108,7 +108,7 @@ class AssetBulkUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? timeZone;
|
||||
Optional<String?> timeZone;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -116,7 +116,7 @@ class AssetBulkUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetVisibility? visibility;
|
||||
Optional<AssetVisibility?> visibility;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AssetBulkUpdateDto &&
|
||||
@@ -152,56 +152,46 @@ class AssetBulkUpdateDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.dateTimeOriginal != null) {
|
||||
json[r'dateTimeOriginal'] = this.dateTimeOriginal;
|
||||
} else {
|
||||
// json[r'dateTimeOriginal'] = null;
|
||||
if (this.dateTimeOriginal.isPresent) {
|
||||
final value = this.dateTimeOriginal.value;
|
||||
json[r'dateTimeOriginal'] = value;
|
||||
}
|
||||
if (this.dateTimeRelative != null) {
|
||||
json[r'dateTimeRelative'] = this.dateTimeRelative;
|
||||
} else {
|
||||
// json[r'dateTimeRelative'] = null;
|
||||
if (this.dateTimeRelative.isPresent) {
|
||||
final value = this.dateTimeRelative.value;
|
||||
json[r'dateTimeRelative'] = value;
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
} else {
|
||||
// json[r'description'] = null;
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
if (this.duplicateId != null) {
|
||||
json[r'duplicateId'] = this.duplicateId;
|
||||
} else {
|
||||
// json[r'duplicateId'] = null;
|
||||
if (this.duplicateId.isPresent) {
|
||||
final value = this.duplicateId.value;
|
||||
json[r'duplicateId'] = value;
|
||||
}
|
||||
json[r'ids'] = this.ids;
|
||||
if (this.isFavorite != null) {
|
||||
json[r'isFavorite'] = this.isFavorite;
|
||||
} else {
|
||||
// json[r'isFavorite'] = null;
|
||||
if (this.isFavorite.isPresent) {
|
||||
final value = this.isFavorite.value;
|
||||
json[r'isFavorite'] = value;
|
||||
}
|
||||
if (this.latitude != null) {
|
||||
json[r'latitude'] = this.latitude;
|
||||
} else {
|
||||
// json[r'latitude'] = null;
|
||||
if (this.latitude.isPresent) {
|
||||
final value = this.latitude.value;
|
||||
json[r'latitude'] = value;
|
||||
}
|
||||
if (this.longitude != null) {
|
||||
json[r'longitude'] = this.longitude;
|
||||
} else {
|
||||
// json[r'longitude'] = null;
|
||||
if (this.longitude.isPresent) {
|
||||
final value = this.longitude.value;
|
||||
json[r'longitude'] = value;
|
||||
}
|
||||
if (this.rating != null) {
|
||||
json[r'rating'] = this.rating;
|
||||
} else {
|
||||
// json[r'rating'] = null;
|
||||
if (this.rating.isPresent) {
|
||||
final value = this.rating.value;
|
||||
json[r'rating'] = value;
|
||||
}
|
||||
if (this.timeZone != null) {
|
||||
json[r'timeZone'] = this.timeZone;
|
||||
} else {
|
||||
// json[r'timeZone'] = null;
|
||||
if (this.timeZone.isPresent) {
|
||||
final value = this.timeZone.value;
|
||||
json[r'timeZone'] = value;
|
||||
}
|
||||
if (this.visibility != null) {
|
||||
json[r'visibility'] = this.visibility;
|
||||
} else {
|
||||
// json[r'visibility'] = null;
|
||||
if (this.visibility.isPresent) {
|
||||
final value = this.visibility.value;
|
||||
json[r'visibility'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -215,19 +205,19 @@ class AssetBulkUpdateDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AssetBulkUpdateDto(
|
||||
dateTimeOriginal: mapValueOfType<String>(json, r'dateTimeOriginal'),
|
||||
dateTimeRelative: mapValueOfType<int>(json, r'dateTimeRelative'),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
duplicateId: mapValueOfType<String>(json, r'duplicateId'),
|
||||
dateTimeOriginal: json.containsKey(r'dateTimeOriginal') ? Optional.present(mapValueOfType<String>(json, r'dateTimeOriginal')) : const Optional.absent(),
|
||||
dateTimeRelative: json.containsKey(r'dateTimeRelative') ? Optional.present(json[r'dateTimeRelative'] == null ? null : int.parse('${json[r'dateTimeRelative']}')) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(mapValueOfType<String>(json, r'description')) : const Optional.absent(),
|
||||
duplicateId: json.containsKey(r'duplicateId') ? Optional.present(mapValueOfType<String>(json, r'duplicateId')) : const Optional.absent(),
|
||||
ids: json[r'ids'] is Iterable
|
||||
? (json[r'ids'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
|
||||
latitude: num.parse('${json[r'latitude']}'),
|
||||
longitude: num.parse('${json[r'longitude']}'),
|
||||
rating: mapValueOfType<int>(json, r'rating'),
|
||||
timeZone: mapValueOfType<String>(json, r'timeZone'),
|
||||
visibility: AssetVisibility.fromJson(json[r'visibility']),
|
||||
isFavorite: json.containsKey(r'isFavorite') ? Optional.present(mapValueOfType<bool>(json, r'isFavorite')) : const Optional.absent(),
|
||||
latitude: json.containsKey(r'latitude') ? Optional.present(json[r'latitude'] == null ? null : num.parse('${json[r'latitude']}')) : const Optional.absent(),
|
||||
longitude: json.containsKey(r'longitude') ? Optional.present(json[r'longitude'] == null ? null : num.parse('${json[r'longitude']}')) : const Optional.absent(),
|
||||
rating: json.containsKey(r'rating') ? Optional.present(json[r'rating'] == null ? null : int.parse('${json[r'rating']}')) : const Optional.absent(),
|
||||
timeZone: json.containsKey(r'timeZone') ? Optional.present(mapValueOfType<String>(json, r'timeZone')) : const Optional.absent(),
|
||||
visibility: json.containsKey(r'visibility') ? Optional.present(AssetVisibility.fromJson(json[r'visibility'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+18
-21
@@ -14,10 +14,10 @@ class AssetBulkUploadCheckResult {
|
||||
/// Returns a new [AssetBulkUploadCheckResult] instance.
|
||||
AssetBulkUploadCheckResult({
|
||||
required this.action,
|
||||
this.assetId,
|
||||
this.assetId = const Optional.absent(),
|
||||
required this.id,
|
||||
this.isTrashed,
|
||||
this.reason,
|
||||
this.isTrashed = const Optional.absent(),
|
||||
this.reason = const Optional.absent(),
|
||||
});
|
||||
|
||||
AssetUploadAction action;
|
||||
@@ -29,7 +29,7 @@ class AssetBulkUploadCheckResult {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? assetId;
|
||||
Optional<String?> assetId;
|
||||
|
||||
/// Asset ID
|
||||
String id;
|
||||
@@ -41,7 +41,7 @@ class AssetBulkUploadCheckResult {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isTrashed;
|
||||
Optional<bool?> isTrashed;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -49,7 +49,7 @@ class AssetBulkUploadCheckResult {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetRejectReason? reason;
|
||||
Optional<AssetRejectReason?> reason;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AssetBulkUploadCheckResult &&
|
||||
@@ -74,21 +74,18 @@ class AssetBulkUploadCheckResult {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'action'] = this.action;
|
||||
if (this.assetId != null) {
|
||||
json[r'assetId'] = this.assetId;
|
||||
} else {
|
||||
// json[r'assetId'] = null;
|
||||
if (this.assetId.isPresent) {
|
||||
final value = this.assetId.value;
|
||||
json[r'assetId'] = value;
|
||||
}
|
||||
json[r'id'] = this.id;
|
||||
if (this.isTrashed != null) {
|
||||
json[r'isTrashed'] = this.isTrashed;
|
||||
} else {
|
||||
// json[r'isTrashed'] = null;
|
||||
if (this.isTrashed.isPresent) {
|
||||
final value = this.isTrashed.value;
|
||||
json[r'isTrashed'] = value;
|
||||
}
|
||||
if (this.reason != null) {
|
||||
json[r'reason'] = this.reason;
|
||||
} else {
|
||||
// json[r'reason'] = null;
|
||||
if (this.reason.isPresent) {
|
||||
final value = this.reason.value;
|
||||
json[r'reason'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -103,10 +100,10 @@ class AssetBulkUploadCheckResult {
|
||||
|
||||
return AssetBulkUploadCheckResult(
|
||||
action: AssetUploadAction.fromJson(json[r'action'])!,
|
||||
assetId: mapValueOfType<String>(json, r'assetId'),
|
||||
assetId: json.containsKey(r'assetId') ? Optional.present(mapValueOfType<String>(json, r'assetId')) : const Optional.absent(),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isTrashed: mapValueOfType<bool>(json, r'isTrashed'),
|
||||
reason: AssetRejectReason.fromJson(json[r'reason']),
|
||||
isTrashed: json.containsKey(r'isTrashed') ? Optional.present(mapValueOfType<bool>(json, r'isTrashed')) : const Optional.absent(),
|
||||
reason: json.containsKey(r'reason') ? Optional.present(AssetRejectReason.fromJson(json[r'reason'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+35
-20
@@ -13,32 +13,32 @@ part of openapi.api;
|
||||
class AssetCopyDto {
|
||||
/// Returns a new [AssetCopyDto] instance.
|
||||
AssetCopyDto({
|
||||
this.albums = true,
|
||||
this.favorite = true,
|
||||
this.sharedLinks = true,
|
||||
this.sidecar = true,
|
||||
this.albums = const Optional.present(true),
|
||||
this.favorite = const Optional.present(true),
|
||||
this.sharedLinks = const Optional.present(true),
|
||||
this.sidecar = const Optional.present(true),
|
||||
required this.sourceId,
|
||||
this.stack = true,
|
||||
this.stack = const Optional.present(true),
|
||||
required this.targetId,
|
||||
});
|
||||
|
||||
/// Copy album associations
|
||||
bool albums;
|
||||
Optional<bool?> albums;
|
||||
|
||||
/// Copy favorite status
|
||||
bool favorite;
|
||||
Optional<bool?> favorite;
|
||||
|
||||
/// Copy shared links
|
||||
bool sharedLinks;
|
||||
Optional<bool?> sharedLinks;
|
||||
|
||||
/// Copy sidecar file
|
||||
bool sidecar;
|
||||
Optional<bool?> sidecar;
|
||||
|
||||
/// Source asset ID
|
||||
String sourceId;
|
||||
|
||||
/// Copy stack association
|
||||
bool stack;
|
||||
Optional<bool?> stack;
|
||||
|
||||
/// Target asset ID
|
||||
String targetId;
|
||||
@@ -69,12 +69,27 @@ class AssetCopyDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'albums'] = this.albums;
|
||||
json[r'favorite'] = this.favorite;
|
||||
json[r'sharedLinks'] = this.sharedLinks;
|
||||
json[r'sidecar'] = this.sidecar;
|
||||
if (this.albums.isPresent) {
|
||||
final value = this.albums.value;
|
||||
json[r'albums'] = value;
|
||||
}
|
||||
if (this.favorite.isPresent) {
|
||||
final value = this.favorite.value;
|
||||
json[r'favorite'] = value;
|
||||
}
|
||||
if (this.sharedLinks.isPresent) {
|
||||
final value = this.sharedLinks.value;
|
||||
json[r'sharedLinks'] = value;
|
||||
}
|
||||
if (this.sidecar.isPresent) {
|
||||
final value = this.sidecar.value;
|
||||
json[r'sidecar'] = value;
|
||||
}
|
||||
json[r'sourceId'] = this.sourceId;
|
||||
json[r'stack'] = this.stack;
|
||||
if (this.stack.isPresent) {
|
||||
final value = this.stack.value;
|
||||
json[r'stack'] = value;
|
||||
}
|
||||
json[r'targetId'] = this.targetId;
|
||||
return json;
|
||||
}
|
||||
@@ -88,12 +103,12 @@ class AssetCopyDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AssetCopyDto(
|
||||
albums: mapValueOfType<bool>(json, r'albums') ?? true,
|
||||
favorite: mapValueOfType<bool>(json, r'favorite') ?? true,
|
||||
sharedLinks: mapValueOfType<bool>(json, r'sharedLinks') ?? true,
|
||||
sidecar: mapValueOfType<bool>(json, r'sidecar') ?? true,
|
||||
albums: json.containsKey(r'albums') ? Optional.present(mapValueOfType<bool>(json, r'albums')) : const Optional.absent(),
|
||||
favorite: json.containsKey(r'favorite') ? Optional.present(mapValueOfType<bool>(json, r'favorite')) : const Optional.absent(),
|
||||
sharedLinks: json.containsKey(r'sharedLinks') ? Optional.present(mapValueOfType<bool>(json, r'sharedLinks')) : const Optional.absent(),
|
||||
sidecar: json.containsKey(r'sidecar') ? Optional.present(mapValueOfType<bool>(json, r'sidecar')) : const Optional.absent(),
|
||||
sourceId: mapValueOfType<String>(json, r'sourceId')!,
|
||||
stack: mapValueOfType<bool>(json, r'stack') ?? true,
|
||||
stack: json.containsKey(r'stack') ? Optional.present(mapValueOfType<bool>(json, r'stack')) : const Optional.absent(),
|
||||
targetId: mapValueOfType<String>(json, r'targetId')!,
|
||||
);
|
||||
}
|
||||
|
||||
+7
-8
@@ -21,7 +21,7 @@ class AssetFaceResponseDto {
|
||||
required this.imageHeight,
|
||||
required this.imageWidth,
|
||||
required this.person,
|
||||
this.sourceType,
|
||||
this.sourceType = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Bounding box X1 coordinate
|
||||
@@ -71,7 +71,7 @@ class AssetFaceResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
SourceType? sourceType;
|
||||
Optional<SourceType?> sourceType;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AssetFaceResponseDto &&
|
||||
@@ -113,12 +113,11 @@ class AssetFaceResponseDto {
|
||||
if (this.person != null) {
|
||||
json[r'person'] = this.person;
|
||||
} else {
|
||||
// json[r'person'] = null;
|
||||
json[r'person'] = null;
|
||||
}
|
||||
if (this.sourceType != null) {
|
||||
json[r'sourceType'] = this.sourceType;
|
||||
} else {
|
||||
// json[r'sourceType'] = null;
|
||||
if (this.sourceType.isPresent) {
|
||||
final value = this.sourceType.value;
|
||||
json[r'sourceType'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -140,7 +139,7 @@ class AssetFaceResponseDto {
|
||||
imageHeight: mapValueOfType<int>(json, r'imageHeight')!,
|
||||
imageWidth: mapValueOfType<int>(json, r'imageWidth')!,
|
||||
person: PersonResponseDto.fromJson(json[r'person']),
|
||||
sourceType: SourceType.fromJson(json[r'sourceType']),
|
||||
sourceType: json.containsKey(r'sourceType') ? Optional.present(SourceType.fromJson(json[r'sourceType'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+6
-7
@@ -14,7 +14,7 @@ class AssetIdsResponseDto {
|
||||
/// Returns a new [AssetIdsResponseDto] instance.
|
||||
AssetIdsResponseDto({
|
||||
required this.assetId,
|
||||
this.error,
|
||||
this.error = const Optional.absent(),
|
||||
required this.success,
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ class AssetIdsResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetIdErrorReason? error;
|
||||
Optional<AssetIdErrorReason?> error;
|
||||
|
||||
/// Whether operation succeeded
|
||||
bool success;
|
||||
@@ -51,10 +51,9 @@ class AssetIdsResponseDto {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'assetId'] = this.assetId;
|
||||
if (this.error != null) {
|
||||
json[r'error'] = this.error;
|
||||
} else {
|
||||
// json[r'error'] = null;
|
||||
if (this.error.isPresent) {
|
||||
final value = this.error.value;
|
||||
json[r'error'] = value;
|
||||
}
|
||||
json[r'success'] = this.success;
|
||||
return json;
|
||||
@@ -70,7 +69,7 @@ class AssetIdsResponseDto {
|
||||
|
||||
return AssetIdsResponseDto(
|
||||
assetId: mapValueOfType<String>(json, r'assetId')!,
|
||||
error: AssetIdErrorReason.fromJson(json[r'error']),
|
||||
error: json.containsKey(r'error') ? Optional.present(AssetIdErrorReason.fromJson(json[r'error'])) : const Optional.absent(),
|
||||
success: mapValueOfType<bool>(json, r'success')!,
|
||||
);
|
||||
}
|
||||
|
||||
+10
-10
@@ -129,18 +129,18 @@ class AssetOcrResponseDto {
|
||||
|
||||
return AssetOcrResponseDto(
|
||||
assetId: mapValueOfType<String>(json, r'assetId')!,
|
||||
boxScore: (mapValueOfType<num>(json, r'boxScore')!).toDouble(),
|
||||
boxScore: mapValueOfType<double>(json, r'boxScore')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
text: mapValueOfType<String>(json, r'text')!,
|
||||
textScore: (mapValueOfType<num>(json, r'textScore')!).toDouble(),
|
||||
x1: (mapValueOfType<num>(json, r'x1')!).toDouble(),
|
||||
x2: (mapValueOfType<num>(json, r'x2')!).toDouble(),
|
||||
x3: (mapValueOfType<num>(json, r'x3')!).toDouble(),
|
||||
x4: (mapValueOfType<num>(json, r'x4')!).toDouble(),
|
||||
y1: (mapValueOfType<num>(json, r'y1')!).toDouble(),
|
||||
y2: (mapValueOfType<num>(json, r'y2')!).toDouble(),
|
||||
y3: (mapValueOfType<num>(json, r'y3')!).toDouble(),
|
||||
y4: (mapValueOfType<num>(json, r'y4')!).toDouble(),
|
||||
textScore: mapValueOfType<double>(json, r'textScore')!,
|
||||
x1: mapValueOfType<double>(json, r'x1')!,
|
||||
x2: mapValueOfType<double>(json, r'x2')!,
|
||||
x3: mapValueOfType<double>(json, r'x3')!,
|
||||
x4: mapValueOfType<double>(json, r'x4')!,
|
||||
y1: mapValueOfType<double>(json, r'y1')!,
|
||||
y2: mapValueOfType<double>(json, r'y2')!,
|
||||
y3: mapValueOfType<double>(json, r'y3')!,
|
||||
y4: mapValueOfType<double>(json, r'y4')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+66
-68
@@ -15,9 +15,9 @@ class AssetResponseDto {
|
||||
AssetResponseDto({
|
||||
required this.checksum,
|
||||
required this.createdAt,
|
||||
this.duplicateId,
|
||||
this.duplicateId = const Optional.absent(),
|
||||
required this.duration,
|
||||
this.exifInfo,
|
||||
this.exifInfo = const Optional.absent(),
|
||||
required this.fileCreatedAt,
|
||||
required this.fileModifiedAt,
|
||||
required this.hasMetadata,
|
||||
@@ -28,18 +28,18 @@ class AssetResponseDto {
|
||||
required this.isFavorite,
|
||||
required this.isOffline,
|
||||
required this.isTrashed,
|
||||
this.libraryId,
|
||||
this.livePhotoVideoId,
|
||||
this.libraryId = const Optional.absent(),
|
||||
this.livePhotoVideoId = const Optional.absent(),
|
||||
required this.localDateTime,
|
||||
required this.originalFileName,
|
||||
this.originalMimeType,
|
||||
this.originalMimeType = const Optional.absent(),
|
||||
required this.originalPath,
|
||||
this.owner,
|
||||
this.owner = const Optional.absent(),
|
||||
required this.ownerId,
|
||||
this.people = const [],
|
||||
this.resized,
|
||||
this.stack,
|
||||
this.tags = const [],
|
||||
this.people = const Optional.present(const []),
|
||||
this.resized = const Optional.absent(),
|
||||
this.stack = const Optional.absent(),
|
||||
this.tags = const Optional.present(const []),
|
||||
required this.thumbhash,
|
||||
required this.type,
|
||||
required this.updatedAt,
|
||||
@@ -54,7 +54,7 @@ class AssetResponseDto {
|
||||
DateTime createdAt;
|
||||
|
||||
/// Duplicate group ID
|
||||
String? duplicateId;
|
||||
Optional<String?> duplicateId;
|
||||
|
||||
/// Video/gif duration in milliseconds (null for static images)
|
||||
///
|
||||
@@ -68,7 +68,7 @@ class AssetResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
ExifResponseDto? exifInfo;
|
||||
Optional<ExifResponseDto?> exifInfo;
|
||||
|
||||
/// The actual UTC timestamp when the file was created/captured, preserving timezone information. This is the authoritative timestamp for chronological sorting within timeline groups. Combined with timezone data, this can be used to determine the exact moment the photo was taken.
|
||||
DateTime fileCreatedAt;
|
||||
@@ -104,10 +104,10 @@ class AssetResponseDto {
|
||||
bool isTrashed;
|
||||
|
||||
/// Library ID
|
||||
String? libraryId;
|
||||
Optional<String?> libraryId;
|
||||
|
||||
/// Live photo video ID
|
||||
String? livePhotoVideoId;
|
||||
Optional<String?> livePhotoVideoId;
|
||||
|
||||
/// The local date and time when the photo/video was taken, derived from EXIF metadata. This represents the photographer's local time regardless of timezone, stored as a timezone-agnostic timestamp. Used for timeline grouping by \"local\" days and months.
|
||||
DateTime localDateTime;
|
||||
@@ -122,7 +122,7 @@ class AssetResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? originalMimeType;
|
||||
Optional<String?> originalMimeType;
|
||||
|
||||
/// Original file path
|
||||
String originalPath;
|
||||
@@ -133,12 +133,12 @@ class AssetResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
UserResponseDto? owner;
|
||||
Optional<UserResponseDto?> owner;
|
||||
|
||||
/// Owner user ID
|
||||
String ownerId;
|
||||
|
||||
List<PersonResponseDto> people;
|
||||
Optional<List<PersonResponseDto>?> people;
|
||||
|
||||
/// Is resized
|
||||
///
|
||||
@@ -147,11 +147,11 @@ class AssetResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? resized;
|
||||
Optional<bool?> resized;
|
||||
|
||||
AssetStackResponseDto? stack;
|
||||
Optional<AssetStackResponseDto?> stack;
|
||||
|
||||
List<TagResponseDto> tags;
|
||||
Optional<List<TagResponseDto>?> tags;
|
||||
|
||||
/// Thumbhash for thumbnail generation (base64) also used as the c query param for thumbnail cache busting.
|
||||
String? thumbhash;
|
||||
@@ -247,20 +247,18 @@ class AssetResponseDto {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'checksum'] = this.checksum;
|
||||
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
|
||||
if (this.duplicateId != null) {
|
||||
json[r'duplicateId'] = this.duplicateId;
|
||||
} else {
|
||||
// json[r'duplicateId'] = null;
|
||||
if (this.duplicateId.isPresent) {
|
||||
final value = this.duplicateId.value;
|
||||
json[r'duplicateId'] = value;
|
||||
}
|
||||
if (this.duration != null) {
|
||||
json[r'duration'] = this.duration;
|
||||
} else {
|
||||
// json[r'duration'] = null;
|
||||
json[r'duration'] = null;
|
||||
}
|
||||
if (this.exifInfo != null) {
|
||||
json[r'exifInfo'] = this.exifInfo;
|
||||
} else {
|
||||
// json[r'exifInfo'] = null;
|
||||
if (this.exifInfo.isPresent) {
|
||||
final value = this.exifInfo.value;
|
||||
json[r'exifInfo'] = value;
|
||||
}
|
||||
json[r'fileCreatedAt'] = this.fileCreatedAt.toUtc().toIso8601String();
|
||||
json[r'fileModifiedAt'] = this.fileModifiedAt.toUtc().toIso8601String();
|
||||
@@ -268,7 +266,7 @@ class AssetResponseDto {
|
||||
if (this.height != null) {
|
||||
json[r'height'] = this.height;
|
||||
} else {
|
||||
// json[r'height'] = null;
|
||||
json[r'height'] = null;
|
||||
}
|
||||
json[r'id'] = this.id;
|
||||
json[r'isArchived'] = this.isArchived;
|
||||
@@ -276,46 +274,46 @@ class AssetResponseDto {
|
||||
json[r'isFavorite'] = this.isFavorite;
|
||||
json[r'isOffline'] = this.isOffline;
|
||||
json[r'isTrashed'] = this.isTrashed;
|
||||
if (this.libraryId != null) {
|
||||
json[r'libraryId'] = this.libraryId;
|
||||
} else {
|
||||
// json[r'libraryId'] = null;
|
||||
if (this.libraryId.isPresent) {
|
||||
final value = this.libraryId.value;
|
||||
json[r'libraryId'] = value;
|
||||
}
|
||||
if (this.livePhotoVideoId != null) {
|
||||
json[r'livePhotoVideoId'] = this.livePhotoVideoId;
|
||||
} else {
|
||||
// json[r'livePhotoVideoId'] = null;
|
||||
if (this.livePhotoVideoId.isPresent) {
|
||||
final value = this.livePhotoVideoId.value;
|
||||
json[r'livePhotoVideoId'] = value;
|
||||
}
|
||||
json[r'localDateTime'] = this.localDateTime.toUtc().toIso8601String();
|
||||
json[r'originalFileName'] = this.originalFileName;
|
||||
if (this.originalMimeType != null) {
|
||||
json[r'originalMimeType'] = this.originalMimeType;
|
||||
} else {
|
||||
// json[r'originalMimeType'] = null;
|
||||
if (this.originalMimeType.isPresent) {
|
||||
final value = this.originalMimeType.value;
|
||||
json[r'originalMimeType'] = value;
|
||||
}
|
||||
json[r'originalPath'] = this.originalPath;
|
||||
if (this.owner != null) {
|
||||
json[r'owner'] = this.owner;
|
||||
} else {
|
||||
// json[r'owner'] = null;
|
||||
if (this.owner.isPresent) {
|
||||
final value = this.owner.value;
|
||||
json[r'owner'] = value;
|
||||
}
|
||||
json[r'ownerId'] = this.ownerId;
|
||||
json[r'people'] = this.people;
|
||||
if (this.resized != null) {
|
||||
json[r'resized'] = this.resized;
|
||||
} else {
|
||||
// json[r'resized'] = null;
|
||||
if (this.people.isPresent) {
|
||||
final value = this.people.value;
|
||||
json[r'people'] = value;
|
||||
}
|
||||
if (this.stack != null) {
|
||||
json[r'stack'] = this.stack;
|
||||
} else {
|
||||
// json[r'stack'] = null;
|
||||
if (this.resized.isPresent) {
|
||||
final value = this.resized.value;
|
||||
json[r'resized'] = value;
|
||||
}
|
||||
if (this.stack.isPresent) {
|
||||
final value = this.stack.value;
|
||||
json[r'stack'] = value;
|
||||
}
|
||||
if (this.tags.isPresent) {
|
||||
final value = this.tags.value;
|
||||
json[r'tags'] = value;
|
||||
}
|
||||
json[r'tags'] = this.tags;
|
||||
if (this.thumbhash != null) {
|
||||
json[r'thumbhash'] = this.thumbhash;
|
||||
} else {
|
||||
// json[r'thumbhash'] = null;
|
||||
json[r'thumbhash'] = null;
|
||||
}
|
||||
json[r'type'] = this.type;
|
||||
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
|
||||
@@ -323,7 +321,7 @@ class AssetResponseDto {
|
||||
if (this.width != null) {
|
||||
json[r'width'] = this.width;
|
||||
} else {
|
||||
// json[r'width'] = null;
|
||||
json[r'width'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -339,9 +337,9 @@ class AssetResponseDto {
|
||||
return AssetResponseDto(
|
||||
checksum: mapValueOfType<String>(json, r'checksum')!,
|
||||
createdAt: mapDateTime(json, r'createdAt', r'')!,
|
||||
duplicateId: mapValueOfType<String>(json, r'duplicateId'),
|
||||
duplicateId: json.containsKey(r'duplicateId') ? Optional.present(mapValueOfType<String>(json, r'duplicateId')) : const Optional.absent(),
|
||||
duration: mapValueOfType<int>(json, r'duration'),
|
||||
exifInfo: ExifResponseDto.fromJson(json[r'exifInfo']),
|
||||
exifInfo: json.containsKey(r'exifInfo') ? Optional.present(ExifResponseDto.fromJson(json[r'exifInfo'])) : const Optional.absent(),
|
||||
fileCreatedAt: mapDateTime(json, r'fileCreatedAt', r'')!,
|
||||
fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r'')!,
|
||||
hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!,
|
||||
@@ -352,18 +350,18 @@ class AssetResponseDto {
|
||||
isFavorite: mapValueOfType<bool>(json, r'isFavorite')!,
|
||||
isOffline: mapValueOfType<bool>(json, r'isOffline')!,
|
||||
isTrashed: mapValueOfType<bool>(json, r'isTrashed')!,
|
||||
libraryId: mapValueOfType<String>(json, r'libraryId'),
|
||||
livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'),
|
||||
libraryId: json.containsKey(r'libraryId') ? Optional.present(mapValueOfType<String>(json, r'libraryId')) : const Optional.absent(),
|
||||
livePhotoVideoId: json.containsKey(r'livePhotoVideoId') ? Optional.present(mapValueOfType<String>(json, r'livePhotoVideoId')) : const Optional.absent(),
|
||||
localDateTime: mapDateTime(json, r'localDateTime', r'')!,
|
||||
originalFileName: mapValueOfType<String>(json, r'originalFileName')!,
|
||||
originalMimeType: mapValueOfType<String>(json, r'originalMimeType'),
|
||||
originalMimeType: json.containsKey(r'originalMimeType') ? Optional.present(mapValueOfType<String>(json, r'originalMimeType')) : const Optional.absent(),
|
||||
originalPath: mapValueOfType<String>(json, r'originalPath')!,
|
||||
owner: UserResponseDto.fromJson(json[r'owner']),
|
||||
owner: json.containsKey(r'owner') ? Optional.present(UserResponseDto.fromJson(json[r'owner'])) : const Optional.absent(),
|
||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||
people: PersonResponseDto.listFromJson(json[r'people']),
|
||||
resized: mapValueOfType<bool>(json, r'resized'),
|
||||
stack: AssetStackResponseDto.fromJson(json[r'stack']),
|
||||
tags: TagResponseDto.listFromJson(json[r'tags']),
|
||||
people: json.containsKey(r'people') ? Optional.present(PersonResponseDto.listFromJson(json[r'people'])) : const Optional.absent(),
|
||||
resized: json.containsKey(r'resized') ? Optional.present(mapValueOfType<bool>(json, r'resized')) : const Optional.absent(),
|
||||
stack: json.containsKey(r'stack') ? Optional.present(AssetStackResponseDto.fromJson(json[r'stack'])) : const Optional.absent(),
|
||||
tags: json.containsKey(r'tags') ? Optional.present(TagResponseDto.listFromJson(json[r'tags'])) : const Optional.absent(),
|
||||
thumbhash: mapValueOfType<String>(json, r'thumbhash'),
|
||||
type: AssetTypeEnum.fromJson(json[r'type'])!,
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
|
||||
|
||||
+12
-14
@@ -13,11 +13,11 @@ part of openapi.api;
|
||||
class AuthStatusResponseDto {
|
||||
/// Returns a new [AuthStatusResponseDto] instance.
|
||||
AuthStatusResponseDto({
|
||||
this.expiresAt,
|
||||
this.expiresAt = const Optional.absent(),
|
||||
required this.isElevated,
|
||||
required this.password,
|
||||
required this.pinCode,
|
||||
this.pinExpiresAt,
|
||||
this.pinExpiresAt = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Session expiration date
|
||||
@@ -27,7 +27,7 @@ class AuthStatusResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? expiresAt;
|
||||
Optional<String?> expiresAt;
|
||||
|
||||
/// Is elevated session
|
||||
bool isElevated;
|
||||
@@ -45,7 +45,7 @@ class AuthStatusResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? pinExpiresAt;
|
||||
Optional<String?> pinExpiresAt;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AuthStatusResponseDto &&
|
||||
@@ -69,18 +69,16 @@ class AuthStatusResponseDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.expiresAt != null) {
|
||||
json[r'expiresAt'] = this.expiresAt;
|
||||
} else {
|
||||
// json[r'expiresAt'] = null;
|
||||
if (this.expiresAt.isPresent) {
|
||||
final value = this.expiresAt.value;
|
||||
json[r'expiresAt'] = value;
|
||||
}
|
||||
json[r'isElevated'] = this.isElevated;
|
||||
json[r'password'] = this.password;
|
||||
json[r'pinCode'] = this.pinCode;
|
||||
if (this.pinExpiresAt != null) {
|
||||
json[r'pinExpiresAt'] = this.pinExpiresAt;
|
||||
} else {
|
||||
// json[r'pinExpiresAt'] = null;
|
||||
if (this.pinExpiresAt.isPresent) {
|
||||
final value = this.pinExpiresAt.value;
|
||||
json[r'pinExpiresAt'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -94,11 +92,11 @@ class AuthStatusResponseDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AuthStatusResponseDto(
|
||||
expiresAt: mapValueOfType<String>(json, r'expiresAt'),
|
||||
expiresAt: json.containsKey(r'expiresAt') ? Optional.present(mapValueOfType<String>(json, r'expiresAt')) : const Optional.absent(),
|
||||
isElevated: mapValueOfType<bool>(json, r'isElevated')!,
|
||||
password: mapValueOfType<bool>(json, r'password')!,
|
||||
pinCode: mapValueOfType<bool>(json, r'pinCode')!,
|
||||
pinExpiresAt: mapValueOfType<String>(json, r'pinExpiresAt'),
|
||||
pinExpiresAt: json.containsKey(r'pinExpiresAt') ? Optional.present(mapValueOfType<String>(json, r'pinExpiresAt')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+6
-7
@@ -13,7 +13,7 @@ part of openapi.api;
|
||||
class AvatarUpdate {
|
||||
/// Returns a new [AvatarUpdate] instance.
|
||||
AvatarUpdate({
|
||||
this.color,
|
||||
this.color = const Optional.absent(),
|
||||
});
|
||||
|
||||
///
|
||||
@@ -22,7 +22,7 @@ class AvatarUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
UserAvatarColor? color;
|
||||
Optional<UserAvatarColor?> color;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AvatarUpdate &&
|
||||
@@ -38,10 +38,9 @@ class AvatarUpdate {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.color != null) {
|
||||
json[r'color'] = this.color;
|
||||
} else {
|
||||
// json[r'color'] = null;
|
||||
if (this.color.isPresent) {
|
||||
final value = this.color.value;
|
||||
json[r'color'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -55,7 +54,7 @@ class AvatarUpdate {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AvatarUpdate(
|
||||
color: UserAvatarColor.fromJson(json[r'color']),
|
||||
color: json.containsKey(r'color') ? Optional.present(UserAvatarColor.fromJson(json[r'color'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+12
-14
@@ -13,8 +13,8 @@ part of openapi.api;
|
||||
class BulkIdResponseDto {
|
||||
/// Returns a new [BulkIdResponseDto] instance.
|
||||
BulkIdResponseDto({
|
||||
this.error,
|
||||
this.errorMessage,
|
||||
this.error = const Optional.absent(),
|
||||
this.errorMessage = const Optional.absent(),
|
||||
required this.id,
|
||||
required this.success,
|
||||
});
|
||||
@@ -25,7 +25,7 @@ class BulkIdResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
BulkIdErrorReason? error;
|
||||
Optional<BulkIdErrorReason?> error;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -33,7 +33,7 @@ class BulkIdResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? errorMessage;
|
||||
Optional<String?> errorMessage;
|
||||
|
||||
/// ID
|
||||
String id;
|
||||
@@ -61,15 +61,13 @@ class BulkIdResponseDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.error != null) {
|
||||
json[r'error'] = this.error;
|
||||
} else {
|
||||
// json[r'error'] = null;
|
||||
if (this.error.isPresent) {
|
||||
final value = this.error.value;
|
||||
json[r'error'] = value;
|
||||
}
|
||||
if (this.errorMessage != null) {
|
||||
json[r'errorMessage'] = this.errorMessage;
|
||||
} else {
|
||||
// json[r'errorMessage'] = null;
|
||||
if (this.errorMessage.isPresent) {
|
||||
final value = this.errorMessage.value;
|
||||
json[r'errorMessage'] = value;
|
||||
}
|
||||
json[r'id'] = this.id;
|
||||
json[r'success'] = this.success;
|
||||
@@ -85,8 +83,8 @@ class BulkIdResponseDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return BulkIdResponseDto(
|
||||
error: BulkIdErrorReason.fromJson(json[r'error']),
|
||||
errorMessage: mapValueOfType<String>(json, r'errorMessage'),
|
||||
error: json.containsKey(r'error') ? Optional.present(BulkIdErrorReason.fromJson(json[r'error'])) : const Optional.absent(),
|
||||
errorMessage: json.containsKey(r'errorMessage') ? Optional.present(mapValueOfType<String>(json, r'errorMessage')) : const Optional.absent(),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
success: mapValueOfType<bool>(json, r'success')!,
|
||||
);
|
||||
|
||||
+6
-7
@@ -13,7 +13,7 @@ part of openapi.api;
|
||||
class CastUpdate {
|
||||
/// Returns a new [CastUpdate] instance.
|
||||
CastUpdate({
|
||||
this.gCastEnabled,
|
||||
this.gCastEnabled = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Whether Google Cast is enabled
|
||||
@@ -23,7 +23,7 @@ class CastUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? gCastEnabled;
|
||||
Optional<bool?> gCastEnabled;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CastUpdate &&
|
||||
@@ -39,10 +39,9 @@ class CastUpdate {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.gCastEnabled != null) {
|
||||
json[r'gCastEnabled'] = this.gCastEnabled;
|
||||
} else {
|
||||
// json[r'gCastEnabled'] = null;
|
||||
if (this.gCastEnabled.isPresent) {
|
||||
final value = this.gCastEnabled.value;
|
||||
json[r'gCastEnabled'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -56,7 +55,7 @@ class CastUpdate {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return CastUpdate(
|
||||
gCastEnabled: mapValueOfType<bool>(json, r'gCastEnabled'),
|
||||
gCastEnabled: json.containsKey(r'gCastEnabled') ? Optional.present(mapValueOfType<bool>(json, r'gCastEnabled')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+7
-4
@@ -13,13 +13,13 @@ part of openapi.api;
|
||||
class ChangePasswordDto {
|
||||
/// Returns a new [ChangePasswordDto] instance.
|
||||
ChangePasswordDto({
|
||||
this.invalidateSessions = false,
|
||||
this.invalidateSessions = const Optional.present(false),
|
||||
required this.newPassword,
|
||||
required this.password,
|
||||
});
|
||||
|
||||
/// Invalidate all other sessions
|
||||
bool invalidateSessions;
|
||||
Optional<bool?> invalidateSessions;
|
||||
|
||||
/// New password (min 8 characters)
|
||||
String newPassword;
|
||||
@@ -45,7 +45,10 @@ class ChangePasswordDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'invalidateSessions'] = this.invalidateSessions;
|
||||
if (this.invalidateSessions.isPresent) {
|
||||
final value = this.invalidateSessions.value;
|
||||
json[r'invalidateSessions'] = value;
|
||||
}
|
||||
json[r'newPassword'] = this.newPassword;
|
||||
json[r'password'] = this.password;
|
||||
return json;
|
||||
@@ -60,7 +63,7 @@ class ChangePasswordDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return ChangePasswordDto(
|
||||
invalidateSessions: mapValueOfType<bool>(json, r'invalidateSessions') ?? false,
|
||||
invalidateSessions: json.containsKey(r'invalidateSessions') ? Optional.present(mapValueOfType<bool>(json, r'invalidateSessions')) : const Optional.absent(),
|
||||
newPassword: mapValueOfType<String>(json, r'newPassword')!,
|
||||
password: mapValueOfType<String>(json, r'password')!,
|
||||
);
|
||||
|
||||
+21
-16
@@ -14,19 +14,19 @@ class CreateAlbumDto {
|
||||
/// Returns a new [CreateAlbumDto] instance.
|
||||
CreateAlbumDto({
|
||||
required this.albumName,
|
||||
this.albumUsers = const [],
|
||||
this.assetIds = const [],
|
||||
this.description,
|
||||
this.albumUsers = const Optional.present(const []),
|
||||
this.assetIds = const Optional.present(const []),
|
||||
this.description = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Album name
|
||||
String albumName;
|
||||
|
||||
/// Album users
|
||||
List<AlbumUserCreateDto> albumUsers;
|
||||
Optional<List<AlbumUserCreateDto>?> albumUsers;
|
||||
|
||||
/// Initial asset IDs
|
||||
List<String> assetIds;
|
||||
Optional<List<String>?> assetIds;
|
||||
|
||||
/// Album description
|
||||
///
|
||||
@@ -35,7 +35,7 @@ class CreateAlbumDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? description;
|
||||
Optional<String?> description;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CreateAlbumDto &&
|
||||
@@ -58,12 +58,17 @@ class CreateAlbumDto {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'albumName'] = this.albumName;
|
||||
json[r'albumUsers'] = this.albumUsers;
|
||||
json[r'assetIds'] = this.assetIds;
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
} else {
|
||||
// json[r'description'] = null;
|
||||
if (this.albumUsers.isPresent) {
|
||||
final value = this.albumUsers.value;
|
||||
json[r'albumUsers'] = value;
|
||||
}
|
||||
if (this.assetIds.isPresent) {
|
||||
final value = this.assetIds.value;
|
||||
json[r'assetIds'] = value;
|
||||
}
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -78,11 +83,11 @@ class CreateAlbumDto {
|
||||
|
||||
return CreateAlbumDto(
|
||||
albumName: mapValueOfType<String>(json, r'albumName')!,
|
||||
albumUsers: AlbumUserCreateDto.listFromJson(json[r'albumUsers']),
|
||||
assetIds: json[r'assetIds'] is Iterable
|
||||
albumUsers: json.containsKey(r'albumUsers') ? Optional.present(AlbumUserCreateDto.listFromJson(json[r'albumUsers'])) : const Optional.absent(),
|
||||
assetIds: json.containsKey(r'assetIds') ? Optional.present(json[r'assetIds'] is Iterable
|
||||
? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
: const []) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(mapValueOfType<String>(json, r'description')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+22
-17
@@ -13,17 +13,17 @@ part of openapi.api;
|
||||
class CreateLibraryDto {
|
||||
/// Returns a new [CreateLibraryDto] instance.
|
||||
CreateLibraryDto({
|
||||
this.exclusionPatterns = const [],
|
||||
this.importPaths = const [],
|
||||
this.name,
|
||||
this.exclusionPatterns = const Optional.present(const []),
|
||||
this.importPaths = const Optional.present(const []),
|
||||
this.name = const Optional.absent(),
|
||||
required this.ownerId,
|
||||
});
|
||||
|
||||
/// Exclusion patterns (max 128)
|
||||
List<String> exclusionPatterns;
|
||||
Optional<List<String>?> exclusionPatterns;
|
||||
|
||||
/// Import paths (max 128)
|
||||
List<String> importPaths;
|
||||
Optional<List<String>?> importPaths;
|
||||
|
||||
/// Library name
|
||||
///
|
||||
@@ -32,7 +32,7 @@ class CreateLibraryDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? name;
|
||||
Optional<String?> name;
|
||||
|
||||
/// Owner user ID
|
||||
String ownerId;
|
||||
@@ -57,12 +57,17 @@ class CreateLibraryDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'exclusionPatterns'] = this.exclusionPatterns;
|
||||
json[r'importPaths'] = this.importPaths;
|
||||
if (this.name != null) {
|
||||
json[r'name'] = this.name;
|
||||
} else {
|
||||
// json[r'name'] = null;
|
||||
if (this.exclusionPatterns.isPresent) {
|
||||
final value = this.exclusionPatterns.value;
|
||||
json[r'exclusionPatterns'] = value;
|
||||
}
|
||||
if (this.importPaths.isPresent) {
|
||||
final value = this.importPaths.value;
|
||||
json[r'importPaths'] = value;
|
||||
}
|
||||
if (this.name.isPresent) {
|
||||
final value = this.name.value;
|
||||
json[r'name'] = value;
|
||||
}
|
||||
json[r'ownerId'] = this.ownerId;
|
||||
return json;
|
||||
@@ -77,13 +82,13 @@ class CreateLibraryDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return CreateLibraryDto(
|
||||
exclusionPatterns: json[r'exclusionPatterns'] is Iterable
|
||||
exclusionPatterns: json.containsKey(r'exclusionPatterns') ? Optional.present(json[r'exclusionPatterns'] is Iterable
|
||||
? (json[r'exclusionPatterns'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
importPaths: json[r'importPaths'] is Iterable
|
||||
: const []) : const Optional.absent(),
|
||||
importPaths: json.containsKey(r'importPaths') ? Optional.present(json[r'importPaths'] is Iterable
|
||||
? (json[r'importPaths'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
name: mapValueOfType<String>(json, r'name'),
|
||||
: const []) : const Optional.absent(),
|
||||
name: json.containsKey(r'name') ? Optional.present(mapValueOfType<String>(json, r'name')) : const Optional.absent(),
|
||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||
);
|
||||
}
|
||||
|
||||
+6
-7
@@ -14,7 +14,7 @@ class DownloadArchiveDto {
|
||||
/// Returns a new [DownloadArchiveDto] instance.
|
||||
DownloadArchiveDto({
|
||||
this.assetIds = const [],
|
||||
this.edited,
|
||||
this.edited = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Asset IDs
|
||||
@@ -27,7 +27,7 @@ class DownloadArchiveDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? edited;
|
||||
Optional<bool?> edited;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is DownloadArchiveDto &&
|
||||
@@ -46,10 +46,9 @@ class DownloadArchiveDto {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'assetIds'] = this.assetIds;
|
||||
if (this.edited != null) {
|
||||
json[r'edited'] = this.edited;
|
||||
} else {
|
||||
// json[r'edited'] = null;
|
||||
if (this.edited.isPresent) {
|
||||
final value = this.edited.value;
|
||||
json[r'edited'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -66,7 +65,7 @@ class DownloadArchiveDto {
|
||||
assetIds: json[r'assetIds'] is Iterable
|
||||
? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
edited: mapValueOfType<bool>(json, r'edited'),
|
||||
edited: json.containsKey(r'edited') ? Optional.present(mapValueOfType<bool>(json, r'edited')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+26
-26
@@ -13,10 +13,10 @@ part of openapi.api;
|
||||
class DownloadInfoDto {
|
||||
/// Returns a new [DownloadInfoDto] instance.
|
||||
DownloadInfoDto({
|
||||
this.albumId,
|
||||
this.archiveSize,
|
||||
this.assetIds = const [],
|
||||
this.userId,
|
||||
this.albumId = const Optional.absent(),
|
||||
this.archiveSize = const Optional.absent(),
|
||||
this.assetIds = const Optional.present(const []),
|
||||
this.userId = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Album ID to download
|
||||
@@ -26,7 +26,7 @@ class DownloadInfoDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? albumId;
|
||||
Optional<String?> albumId;
|
||||
|
||||
/// Archive size limit in bytes
|
||||
///
|
||||
@@ -38,10 +38,10 @@ class DownloadInfoDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? archiveSize;
|
||||
Optional<int?> archiveSize;
|
||||
|
||||
/// Asset IDs to download
|
||||
List<String> assetIds;
|
||||
Optional<List<String>?> assetIds;
|
||||
|
||||
/// User ID to download assets from
|
||||
///
|
||||
@@ -50,7 +50,7 @@ class DownloadInfoDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? userId;
|
||||
Optional<String?> userId;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is DownloadInfoDto &&
|
||||
@@ -72,21 +72,21 @@ class DownloadInfoDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.albumId != null) {
|
||||
json[r'albumId'] = this.albumId;
|
||||
} else {
|
||||
// json[r'albumId'] = null;
|
||||
if (this.albumId.isPresent) {
|
||||
final value = this.albumId.value;
|
||||
json[r'albumId'] = value;
|
||||
}
|
||||
if (this.archiveSize != null) {
|
||||
json[r'archiveSize'] = this.archiveSize;
|
||||
} else {
|
||||
// json[r'archiveSize'] = null;
|
||||
if (this.archiveSize.isPresent) {
|
||||
final value = this.archiveSize.value;
|
||||
json[r'archiveSize'] = value;
|
||||
}
|
||||
json[r'assetIds'] = this.assetIds;
|
||||
if (this.userId != null) {
|
||||
json[r'userId'] = this.userId;
|
||||
} else {
|
||||
// json[r'userId'] = null;
|
||||
if (this.assetIds.isPresent) {
|
||||
final value = this.assetIds.value;
|
||||
json[r'assetIds'] = value;
|
||||
}
|
||||
if (this.userId.isPresent) {
|
||||
final value = this.userId.value;
|
||||
json[r'userId'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -100,12 +100,12 @@ class DownloadInfoDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return DownloadInfoDto(
|
||||
albumId: mapValueOfType<String>(json, r'albumId'),
|
||||
archiveSize: mapValueOfType<int>(json, r'archiveSize'),
|
||||
assetIds: json[r'assetIds'] is Iterable
|
||||
albumId: json.containsKey(r'albumId') ? Optional.present(mapValueOfType<String>(json, r'albumId')) : const Optional.absent(),
|
||||
archiveSize: json.containsKey(r'archiveSize') ? Optional.present(json[r'archiveSize'] == null ? null : int.parse('${json[r'archiveSize']}')) : const Optional.absent(),
|
||||
assetIds: json.containsKey(r'assetIds') ? Optional.present(json[r'assetIds'] is Iterable
|
||||
? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
userId: mapValueOfType<String>(json, r'userId'),
|
||||
: const []) : const Optional.absent(),
|
||||
userId: json.containsKey(r'userId') ? Optional.present(mapValueOfType<String>(json, r'userId')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+12
-14
@@ -13,8 +13,8 @@ part of openapi.api;
|
||||
class DownloadUpdate {
|
||||
/// Returns a new [DownloadUpdate] instance.
|
||||
DownloadUpdate({
|
||||
this.archiveSize,
|
||||
this.includeEmbeddedVideos,
|
||||
this.archiveSize = const Optional.absent(),
|
||||
this.includeEmbeddedVideos = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Maximum archive size in bytes
|
||||
@@ -27,7 +27,7 @@ class DownloadUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? archiveSize;
|
||||
Optional<int?> archiveSize;
|
||||
|
||||
/// Whether to include embedded videos in downloads
|
||||
///
|
||||
@@ -36,7 +36,7 @@ class DownloadUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? includeEmbeddedVideos;
|
||||
Optional<bool?> includeEmbeddedVideos;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is DownloadUpdate &&
|
||||
@@ -54,15 +54,13 @@ class DownloadUpdate {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.archiveSize != null) {
|
||||
json[r'archiveSize'] = this.archiveSize;
|
||||
} else {
|
||||
// json[r'archiveSize'] = null;
|
||||
if (this.archiveSize.isPresent) {
|
||||
final value = this.archiveSize.value;
|
||||
json[r'archiveSize'] = value;
|
||||
}
|
||||
if (this.includeEmbeddedVideos != null) {
|
||||
json[r'includeEmbeddedVideos'] = this.includeEmbeddedVideos;
|
||||
} else {
|
||||
// json[r'includeEmbeddedVideos'] = null;
|
||||
if (this.includeEmbeddedVideos.isPresent) {
|
||||
final value = this.includeEmbeddedVideos.value;
|
||||
json[r'includeEmbeddedVideos'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -76,8 +74,8 @@ class DownloadUpdate {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return DownloadUpdate(
|
||||
archiveSize: mapValueOfType<int>(json, r'archiveSize'),
|
||||
includeEmbeddedVideos: mapValueOfType<bool>(json, r'includeEmbeddedVideos'),
|
||||
archiveSize: json.containsKey(r'archiveSize') ? Optional.present(json[r'archiveSize'] == null ? null : int.parse('${json[r'archiveSize']}')) : const Optional.absent(),
|
||||
includeEmbeddedVideos: json.containsKey(r'includeEmbeddedVideos') ? Optional.present(mapValueOfType<bool>(json, r'includeEmbeddedVideos')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ class DuplicateDetectionConfig {
|
||||
|
||||
return DuplicateDetectionConfig(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
maxDistance: (mapValueOfType<num>(json, r'maxDistance')!).toDouble(),
|
||||
maxDistance: mapValueOfType<double>(json, r'maxDistance')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+18
-21
@@ -13,9 +13,9 @@ part of openapi.api;
|
||||
class EmailNotificationsUpdate {
|
||||
/// Returns a new [EmailNotificationsUpdate] instance.
|
||||
EmailNotificationsUpdate({
|
||||
this.albumInvite,
|
||||
this.albumUpdate,
|
||||
this.enabled,
|
||||
this.albumInvite = const Optional.absent(),
|
||||
this.albumUpdate = const Optional.absent(),
|
||||
this.enabled = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Whether to receive email notifications for album invites
|
||||
@@ -25,7 +25,7 @@ class EmailNotificationsUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? albumInvite;
|
||||
Optional<bool?> albumInvite;
|
||||
|
||||
/// Whether to receive email notifications for album updates
|
||||
///
|
||||
@@ -34,7 +34,7 @@ class EmailNotificationsUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? albumUpdate;
|
||||
Optional<bool?> albumUpdate;
|
||||
|
||||
/// Whether email notifications are enabled
|
||||
///
|
||||
@@ -43,7 +43,7 @@ class EmailNotificationsUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? enabled;
|
||||
Optional<bool?> enabled;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is EmailNotificationsUpdate &&
|
||||
@@ -63,20 +63,17 @@ class EmailNotificationsUpdate {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.albumInvite != null) {
|
||||
json[r'albumInvite'] = this.albumInvite;
|
||||
} else {
|
||||
// json[r'albumInvite'] = null;
|
||||
if (this.albumInvite.isPresent) {
|
||||
final value = this.albumInvite.value;
|
||||
json[r'albumInvite'] = value;
|
||||
}
|
||||
if (this.albumUpdate != null) {
|
||||
json[r'albumUpdate'] = this.albumUpdate;
|
||||
} else {
|
||||
// json[r'albumUpdate'] = null;
|
||||
if (this.albumUpdate.isPresent) {
|
||||
final value = this.albumUpdate.value;
|
||||
json[r'albumUpdate'] = value;
|
||||
}
|
||||
if (this.enabled != null) {
|
||||
json[r'enabled'] = this.enabled;
|
||||
} else {
|
||||
// json[r'enabled'] = null;
|
||||
if (this.enabled.isPresent) {
|
||||
final value = this.enabled.value;
|
||||
json[r'enabled'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -90,9 +87,9 @@ class EmailNotificationsUpdate {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return EmailNotificationsUpdate(
|
||||
albumInvite: mapValueOfType<bool>(json, r'albumInvite'),
|
||||
albumUpdate: mapValueOfType<bool>(json, r'albumUpdate'),
|
||||
enabled: mapValueOfType<bool>(json, r'enabled'),
|
||||
albumInvite: json.containsKey(r'albumInvite') ? Optional.present(mapValueOfType<bool>(json, r'albumInvite')) : const Optional.absent(),
|
||||
albumUpdate: json.containsKey(r'albumUpdate') ? Optional.present(mapValueOfType<bool>(json, r'albumUpdate')) : const Optional.absent(),
|
||||
enabled: json.containsKey(r'enabled') ? Optional.present(mapValueOfType<bool>(json, r'enabled')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+132
-162
@@ -13,110 +13,110 @@ part of openapi.api;
|
||||
class ExifResponseDto {
|
||||
/// Returns a new [ExifResponseDto] instance.
|
||||
ExifResponseDto({
|
||||
this.city,
|
||||
this.country,
|
||||
this.dateTimeOriginal,
|
||||
this.description,
|
||||
this.exifImageHeight,
|
||||
this.exifImageWidth,
|
||||
this.exposureTime,
|
||||
this.fNumber,
|
||||
this.fileSizeInByte,
|
||||
this.focalLength,
|
||||
this.iso,
|
||||
this.latitude,
|
||||
this.lensModel,
|
||||
this.longitude,
|
||||
this.make,
|
||||
this.model,
|
||||
this.modifyDate,
|
||||
this.orientation,
|
||||
this.projectionType,
|
||||
this.rating,
|
||||
this.state,
|
||||
this.timeZone,
|
||||
this.city = const Optional.absent(),
|
||||
this.country = const Optional.absent(),
|
||||
this.dateTimeOriginal = const Optional.absent(),
|
||||
this.description = const Optional.absent(),
|
||||
this.exifImageHeight = const Optional.absent(),
|
||||
this.exifImageWidth = const Optional.absent(),
|
||||
this.exposureTime = const Optional.absent(),
|
||||
this.fNumber = const Optional.absent(),
|
||||
this.fileSizeInByte = const Optional.absent(),
|
||||
this.focalLength = const Optional.absent(),
|
||||
this.iso = const Optional.absent(),
|
||||
this.latitude = const Optional.absent(),
|
||||
this.lensModel = const Optional.absent(),
|
||||
this.longitude = const Optional.absent(),
|
||||
this.make = const Optional.absent(),
|
||||
this.model = const Optional.absent(),
|
||||
this.modifyDate = const Optional.absent(),
|
||||
this.orientation = const Optional.absent(),
|
||||
this.projectionType = const Optional.absent(),
|
||||
this.rating = const Optional.absent(),
|
||||
this.state = const Optional.absent(),
|
||||
this.timeZone = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// City name
|
||||
String? city;
|
||||
Optional<String?> city;
|
||||
|
||||
/// Country name
|
||||
String? country;
|
||||
Optional<String?> country;
|
||||
|
||||
/// Original date/time
|
||||
DateTime? dateTimeOriginal;
|
||||
Optional<DateTime?> dateTimeOriginal;
|
||||
|
||||
/// Image description
|
||||
String? description;
|
||||
Optional<String?> description;
|
||||
|
||||
/// Image height in pixels
|
||||
///
|
||||
/// Minimum value: 0
|
||||
/// Maximum value: 9007199254740991
|
||||
int? exifImageHeight;
|
||||
Optional<int?> exifImageHeight;
|
||||
|
||||
/// Image width in pixels
|
||||
///
|
||||
/// Minimum value: 0
|
||||
/// Maximum value: 9007199254740991
|
||||
int? exifImageWidth;
|
||||
Optional<int?> exifImageWidth;
|
||||
|
||||
/// Exposure time
|
||||
String? exposureTime;
|
||||
Optional<String?> exposureTime;
|
||||
|
||||
/// F-number (aperture)
|
||||
num? fNumber;
|
||||
Optional<num?> fNumber;
|
||||
|
||||
/// File size in bytes
|
||||
///
|
||||
/// Minimum value: 0
|
||||
/// Maximum value: 9007199254740991
|
||||
int? fileSizeInByte;
|
||||
Optional<int?> fileSizeInByte;
|
||||
|
||||
/// Focal length in mm
|
||||
num? focalLength;
|
||||
Optional<num?> focalLength;
|
||||
|
||||
/// ISO sensitivity
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int? iso;
|
||||
Optional<int?> iso;
|
||||
|
||||
/// GPS latitude
|
||||
num? latitude;
|
||||
Optional<num?> latitude;
|
||||
|
||||
/// Lens model
|
||||
String? lensModel;
|
||||
Optional<String?> lensModel;
|
||||
|
||||
/// GPS longitude
|
||||
num? longitude;
|
||||
Optional<num?> longitude;
|
||||
|
||||
/// Camera make
|
||||
String? make;
|
||||
Optional<String?> make;
|
||||
|
||||
/// Camera model
|
||||
String? model;
|
||||
Optional<String?> model;
|
||||
|
||||
/// Modification date/time
|
||||
DateTime? modifyDate;
|
||||
Optional<DateTime?> modifyDate;
|
||||
|
||||
/// Image orientation
|
||||
String? orientation;
|
||||
Optional<String?> orientation;
|
||||
|
||||
/// Projection type
|
||||
String? projectionType;
|
||||
Optional<String?> projectionType;
|
||||
|
||||
/// Rating
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int? rating;
|
||||
Optional<int?> rating;
|
||||
|
||||
/// State/province name
|
||||
String? state;
|
||||
Optional<String?> state;
|
||||
|
||||
/// Time zone
|
||||
String? timeZone;
|
||||
Optional<String?> timeZone;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is ExifResponseDto &&
|
||||
@@ -174,115 +174,93 @@ class ExifResponseDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.city != null) {
|
||||
json[r'city'] = this.city;
|
||||
} else {
|
||||
// json[r'city'] = null;
|
||||
if (this.city.isPresent) {
|
||||
final value = this.city.value;
|
||||
json[r'city'] = value;
|
||||
}
|
||||
if (this.country != null) {
|
||||
json[r'country'] = this.country;
|
||||
} else {
|
||||
// json[r'country'] = null;
|
||||
if (this.country.isPresent) {
|
||||
final value = this.country.value;
|
||||
json[r'country'] = value;
|
||||
}
|
||||
if (this.dateTimeOriginal != null) {
|
||||
json[r'dateTimeOriginal'] = this.dateTimeOriginal!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'dateTimeOriginal'] = null;
|
||||
if (this.dateTimeOriginal.isPresent) {
|
||||
final value = this.dateTimeOriginal.value;
|
||||
json[r'dateTimeOriginal'] = value == null ? null : value.toUtc().toIso8601String();
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
} else {
|
||||
// json[r'description'] = null;
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
if (this.exifImageHeight != null) {
|
||||
json[r'exifImageHeight'] = this.exifImageHeight;
|
||||
} else {
|
||||
// json[r'exifImageHeight'] = null;
|
||||
if (this.exifImageHeight.isPresent) {
|
||||
final value = this.exifImageHeight.value;
|
||||
json[r'exifImageHeight'] = value;
|
||||
}
|
||||
if (this.exifImageWidth != null) {
|
||||
json[r'exifImageWidth'] = this.exifImageWidth;
|
||||
} else {
|
||||
// json[r'exifImageWidth'] = null;
|
||||
if (this.exifImageWidth.isPresent) {
|
||||
final value = this.exifImageWidth.value;
|
||||
json[r'exifImageWidth'] = value;
|
||||
}
|
||||
if (this.exposureTime != null) {
|
||||
json[r'exposureTime'] = this.exposureTime;
|
||||
} else {
|
||||
// json[r'exposureTime'] = null;
|
||||
if (this.exposureTime.isPresent) {
|
||||
final value = this.exposureTime.value;
|
||||
json[r'exposureTime'] = value;
|
||||
}
|
||||
if (this.fNumber != null) {
|
||||
json[r'fNumber'] = this.fNumber;
|
||||
} else {
|
||||
// json[r'fNumber'] = null;
|
||||
if (this.fNumber.isPresent) {
|
||||
final value = this.fNumber.value;
|
||||
json[r'fNumber'] = value;
|
||||
}
|
||||
if (this.fileSizeInByte != null) {
|
||||
json[r'fileSizeInByte'] = this.fileSizeInByte;
|
||||
} else {
|
||||
// json[r'fileSizeInByte'] = null;
|
||||
if (this.fileSizeInByte.isPresent) {
|
||||
final value = this.fileSizeInByte.value;
|
||||
json[r'fileSizeInByte'] = value;
|
||||
}
|
||||
if (this.focalLength != null) {
|
||||
json[r'focalLength'] = this.focalLength;
|
||||
} else {
|
||||
// json[r'focalLength'] = null;
|
||||
if (this.focalLength.isPresent) {
|
||||
final value = this.focalLength.value;
|
||||
json[r'focalLength'] = value;
|
||||
}
|
||||
if (this.iso != null) {
|
||||
json[r'iso'] = this.iso;
|
||||
} else {
|
||||
// json[r'iso'] = null;
|
||||
if (this.iso.isPresent) {
|
||||
final value = this.iso.value;
|
||||
json[r'iso'] = value;
|
||||
}
|
||||
if (this.latitude != null) {
|
||||
json[r'latitude'] = this.latitude;
|
||||
} else {
|
||||
// json[r'latitude'] = null;
|
||||
if (this.latitude.isPresent) {
|
||||
final value = this.latitude.value;
|
||||
json[r'latitude'] = value;
|
||||
}
|
||||
if (this.lensModel != null) {
|
||||
json[r'lensModel'] = this.lensModel;
|
||||
} else {
|
||||
// json[r'lensModel'] = null;
|
||||
if (this.lensModel.isPresent) {
|
||||
final value = this.lensModel.value;
|
||||
json[r'lensModel'] = value;
|
||||
}
|
||||
if (this.longitude != null) {
|
||||
json[r'longitude'] = this.longitude;
|
||||
} else {
|
||||
// json[r'longitude'] = null;
|
||||
if (this.longitude.isPresent) {
|
||||
final value = this.longitude.value;
|
||||
json[r'longitude'] = value;
|
||||
}
|
||||
if (this.make != null) {
|
||||
json[r'make'] = this.make;
|
||||
} else {
|
||||
// json[r'make'] = null;
|
||||
if (this.make.isPresent) {
|
||||
final value = this.make.value;
|
||||
json[r'make'] = value;
|
||||
}
|
||||
if (this.model != null) {
|
||||
json[r'model'] = this.model;
|
||||
} else {
|
||||
// json[r'model'] = null;
|
||||
if (this.model.isPresent) {
|
||||
final value = this.model.value;
|
||||
json[r'model'] = value;
|
||||
}
|
||||
if (this.modifyDate != null) {
|
||||
json[r'modifyDate'] = this.modifyDate!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'modifyDate'] = null;
|
||||
if (this.modifyDate.isPresent) {
|
||||
final value = this.modifyDate.value;
|
||||
json[r'modifyDate'] = value == null ? null : value.toUtc().toIso8601String();
|
||||
}
|
||||
if (this.orientation != null) {
|
||||
json[r'orientation'] = this.orientation;
|
||||
} else {
|
||||
// json[r'orientation'] = null;
|
||||
if (this.orientation.isPresent) {
|
||||
final value = this.orientation.value;
|
||||
json[r'orientation'] = value;
|
||||
}
|
||||
if (this.projectionType != null) {
|
||||
json[r'projectionType'] = this.projectionType;
|
||||
} else {
|
||||
// json[r'projectionType'] = null;
|
||||
if (this.projectionType.isPresent) {
|
||||
final value = this.projectionType.value;
|
||||
json[r'projectionType'] = value;
|
||||
}
|
||||
if (this.rating != null) {
|
||||
json[r'rating'] = this.rating;
|
||||
} else {
|
||||
// json[r'rating'] = null;
|
||||
if (this.rating.isPresent) {
|
||||
final value = this.rating.value;
|
||||
json[r'rating'] = value;
|
||||
}
|
||||
if (this.state != null) {
|
||||
json[r'state'] = this.state;
|
||||
} else {
|
||||
// json[r'state'] = null;
|
||||
if (this.state.isPresent) {
|
||||
final value = this.state.value;
|
||||
json[r'state'] = value;
|
||||
}
|
||||
if (this.timeZone != null) {
|
||||
json[r'timeZone'] = this.timeZone;
|
||||
} else {
|
||||
// json[r'timeZone'] = null;
|
||||
if (this.timeZone.isPresent) {
|
||||
final value = this.timeZone.value;
|
||||
json[r'timeZone'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -296,36 +274,28 @@ class ExifResponseDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return ExifResponseDto(
|
||||
city: mapValueOfType<String>(json, r'city'),
|
||||
country: mapValueOfType<String>(json, r'country'),
|
||||
dateTimeOriginal: mapDateTime(json, r'dateTimeOriginal', r''),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
exifImageHeight: mapValueOfType<int>(json, r'exifImageHeight'),
|
||||
exifImageWidth: mapValueOfType<int>(json, r'exifImageWidth'),
|
||||
exposureTime: mapValueOfType<String>(json, r'exposureTime'),
|
||||
fNumber: json[r'fNumber'] == null
|
||||
? null
|
||||
: num.parse('${json[r'fNumber']}'),
|
||||
fileSizeInByte: mapValueOfType<int>(json, r'fileSizeInByte'),
|
||||
focalLength: json[r'focalLength'] == null
|
||||
? null
|
||||
: num.parse('${json[r'focalLength']}'),
|
||||
iso: mapValueOfType<int>(json, r'iso'),
|
||||
latitude: json[r'latitude'] == null
|
||||
? null
|
||||
: num.parse('${json[r'latitude']}'),
|
||||
lensModel: mapValueOfType<String>(json, r'lensModel'),
|
||||
longitude: json[r'longitude'] == null
|
||||
? null
|
||||
: num.parse('${json[r'longitude']}'),
|
||||
make: mapValueOfType<String>(json, r'make'),
|
||||
model: mapValueOfType<String>(json, r'model'),
|
||||
modifyDate: mapDateTime(json, r'modifyDate', r''),
|
||||
orientation: mapValueOfType<String>(json, r'orientation'),
|
||||
projectionType: mapValueOfType<String>(json, r'projectionType'),
|
||||
rating: mapValueOfType<int>(json, r'rating'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
timeZone: mapValueOfType<String>(json, r'timeZone'),
|
||||
city: json.containsKey(r'city') ? Optional.present(mapValueOfType<String>(json, r'city')) : const Optional.absent(),
|
||||
country: json.containsKey(r'country') ? Optional.present(mapValueOfType<String>(json, r'country')) : const Optional.absent(),
|
||||
dateTimeOriginal: json.containsKey(r'dateTimeOriginal') ? Optional.present(mapDateTime(json, r'dateTimeOriginal', r'')) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(mapValueOfType<String>(json, r'description')) : const Optional.absent(),
|
||||
exifImageHeight: json.containsKey(r'exifImageHeight') ? Optional.present(json[r'exifImageHeight'] == null ? null : int.parse('${json[r'exifImageHeight']}')) : const Optional.absent(),
|
||||
exifImageWidth: json.containsKey(r'exifImageWidth') ? Optional.present(json[r'exifImageWidth'] == null ? null : int.parse('${json[r'exifImageWidth']}')) : const Optional.absent(),
|
||||
exposureTime: json.containsKey(r'exposureTime') ? Optional.present(mapValueOfType<String>(json, r'exposureTime')) : const Optional.absent(),
|
||||
fNumber: json.containsKey(r'fNumber') ? Optional.present(json[r'fNumber'] == null ? null : num.parse('${json[r'fNumber']}')) : const Optional.absent(),
|
||||
fileSizeInByte: json.containsKey(r'fileSizeInByte') ? Optional.present(json[r'fileSizeInByte'] == null ? null : int.parse('${json[r'fileSizeInByte']}')) : const Optional.absent(),
|
||||
focalLength: json.containsKey(r'focalLength') ? Optional.present(json[r'focalLength'] == null ? null : num.parse('${json[r'focalLength']}')) : const Optional.absent(),
|
||||
iso: json.containsKey(r'iso') ? Optional.present(json[r'iso'] == null ? null : int.parse('${json[r'iso']}')) : const Optional.absent(),
|
||||
latitude: json.containsKey(r'latitude') ? Optional.present(json[r'latitude'] == null ? null : num.parse('${json[r'latitude']}')) : const Optional.absent(),
|
||||
lensModel: json.containsKey(r'lensModel') ? Optional.present(mapValueOfType<String>(json, r'lensModel')) : const Optional.absent(),
|
||||
longitude: json.containsKey(r'longitude') ? Optional.present(json[r'longitude'] == null ? null : num.parse('${json[r'longitude']}')) : const Optional.absent(),
|
||||
make: json.containsKey(r'make') ? Optional.present(mapValueOfType<String>(json, r'make')) : const Optional.absent(),
|
||||
model: json.containsKey(r'model') ? Optional.present(mapValueOfType<String>(json, r'model')) : const Optional.absent(),
|
||||
modifyDate: json.containsKey(r'modifyDate') ? Optional.present(mapDateTime(json, r'modifyDate', r'')) : const Optional.absent(),
|
||||
orientation: json.containsKey(r'orientation') ? Optional.present(mapValueOfType<String>(json, r'orientation')) : const Optional.absent(),
|
||||
projectionType: json.containsKey(r'projectionType') ? Optional.present(mapValueOfType<String>(json, r'projectionType')) : const Optional.absent(),
|
||||
rating: json.containsKey(r'rating') ? Optional.present(json[r'rating'] == null ? null : int.parse('${json[r'rating']}')) : const Optional.absent(),
|
||||
state: json.containsKey(r'state') ? Optional.present(mapValueOfType<String>(json, r'state')) : const Optional.absent(),
|
||||
timeZone: json.containsKey(r'timeZone') ? Optional.present(mapValueOfType<String>(json, r'timeZone')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+2
-2
@@ -84,9 +84,9 @@ class FacialRecognitionConfig {
|
||||
|
||||
return FacialRecognitionConfig(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
maxDistance: (mapValueOfType<num>(json, r'maxDistance')!).toDouble(),
|
||||
maxDistance: mapValueOfType<double>(json, r'maxDistance')!,
|
||||
minFaces: mapValueOfType<int>(json, r'minFaces')!,
|
||||
minScore: (mapValueOfType<num>(json, r'minScore')!).toDouble(),
|
||||
minScore: mapValueOfType<double>(json, r'minScore')!,
|
||||
modelName: mapValueOfType<String>(json, r'modelName')!,
|
||||
);
|
||||
}
|
||||
|
||||
+12
-14
@@ -13,8 +13,8 @@ part of openapi.api;
|
||||
class FoldersUpdate {
|
||||
/// Returns a new [FoldersUpdate] instance.
|
||||
FoldersUpdate({
|
||||
this.enabled,
|
||||
this.sidebarWeb,
|
||||
this.enabled = const Optional.absent(),
|
||||
this.sidebarWeb = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Whether folders are enabled
|
||||
@@ -24,7 +24,7 @@ class FoldersUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? enabled;
|
||||
Optional<bool?> enabled;
|
||||
|
||||
/// Whether folders appear in web sidebar
|
||||
///
|
||||
@@ -33,7 +33,7 @@ class FoldersUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? sidebarWeb;
|
||||
Optional<bool?> sidebarWeb;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is FoldersUpdate &&
|
||||
@@ -51,15 +51,13 @@ class FoldersUpdate {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.enabled != null) {
|
||||
json[r'enabled'] = this.enabled;
|
||||
} else {
|
||||
// json[r'enabled'] = null;
|
||||
if (this.enabled.isPresent) {
|
||||
final value = this.enabled.value;
|
||||
json[r'enabled'] = value;
|
||||
}
|
||||
if (this.sidebarWeb != null) {
|
||||
json[r'sidebarWeb'] = this.sidebarWeb;
|
||||
} else {
|
||||
// json[r'sidebarWeb'] = null;
|
||||
if (this.sidebarWeb.isPresent) {
|
||||
final value = this.sidebarWeb.value;
|
||||
json[r'sidebarWeb'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -73,8 +71,8 @@ class FoldersUpdate {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return FoldersUpdate(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled'),
|
||||
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb'),
|
||||
enabled: json.containsKey(r'enabled') ? Optional.present(mapValueOfType<bool>(json, r'enabled')) : const Optional.absent(),
|
||||
sidebarWeb: json.containsKey(r'sidebarWeb') ? Optional.present(mapValueOfType<bool>(json, r'sidebarWeb')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ class LibraryResponseDto {
|
||||
? this.refreshedAt!.millisecondsSinceEpoch
|
||||
: this.refreshedAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'refreshedAt'] = null;
|
||||
json[r'refreshedAt'] = null;
|
||||
}
|
||||
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedAt.millisecondsSinceEpoch
|
||||
|
||||
+6
-7
@@ -13,7 +13,7 @@ part of openapi.api;
|
||||
class MaintenanceLoginDto {
|
||||
/// Returns a new [MaintenanceLoginDto] instance.
|
||||
MaintenanceLoginDto({
|
||||
this.token,
|
||||
this.token = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Maintenance token
|
||||
@@ -23,7 +23,7 @@ class MaintenanceLoginDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? token;
|
||||
Optional<String?> token;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MaintenanceLoginDto &&
|
||||
@@ -39,10 +39,9 @@ class MaintenanceLoginDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.token != null) {
|
||||
json[r'token'] = this.token;
|
||||
} else {
|
||||
// json[r'token'] = null;
|
||||
if (this.token.isPresent) {
|
||||
final value = this.token.value;
|
||||
json[r'token'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -56,7 +55,7 @@ class MaintenanceLoginDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MaintenanceLoginDto(
|
||||
token: mapValueOfType<String>(json, r'token'),
|
||||
token: json.containsKey(r'token') ? Optional.present(mapValueOfType<String>(json, r'token')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+18
-21
@@ -15,9 +15,9 @@ class MaintenanceStatusResponseDto {
|
||||
MaintenanceStatusResponseDto({
|
||||
required this.action,
|
||||
required this.active,
|
||||
this.error,
|
||||
this.progress,
|
||||
this.task,
|
||||
this.error = const Optional.absent(),
|
||||
this.progress = const Optional.absent(),
|
||||
this.task = const Optional.absent(),
|
||||
});
|
||||
|
||||
MaintenanceAction action;
|
||||
@@ -30,7 +30,7 @@ class MaintenanceStatusResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? error;
|
||||
Optional<String?> error;
|
||||
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
@@ -40,7 +40,7 @@ class MaintenanceStatusResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? progress;
|
||||
Optional<int?> progress;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -48,7 +48,7 @@ class MaintenanceStatusResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? task;
|
||||
Optional<String?> task;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MaintenanceStatusResponseDto &&
|
||||
@@ -74,20 +74,17 @@ class MaintenanceStatusResponseDto {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'action'] = this.action;
|
||||
json[r'active'] = this.active;
|
||||
if (this.error != null) {
|
||||
json[r'error'] = this.error;
|
||||
} else {
|
||||
// json[r'error'] = null;
|
||||
if (this.error.isPresent) {
|
||||
final value = this.error.value;
|
||||
json[r'error'] = value;
|
||||
}
|
||||
if (this.progress != null) {
|
||||
json[r'progress'] = this.progress;
|
||||
} else {
|
||||
// json[r'progress'] = null;
|
||||
if (this.progress.isPresent) {
|
||||
final value = this.progress.value;
|
||||
json[r'progress'] = value;
|
||||
}
|
||||
if (this.task != null) {
|
||||
json[r'task'] = this.task;
|
||||
} else {
|
||||
// json[r'task'] = null;
|
||||
if (this.task.isPresent) {
|
||||
final value = this.task.value;
|
||||
json[r'task'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -103,9 +100,9 @@ class MaintenanceStatusResponseDto {
|
||||
return MaintenanceStatusResponseDto(
|
||||
action: MaintenanceAction.fromJson(json[r'action'])!,
|
||||
active: mapValueOfType<bool>(json, r'active')!,
|
||||
error: mapValueOfType<String>(json, r'error'),
|
||||
progress: mapValueOfType<int>(json, r'progress'),
|
||||
task: mapValueOfType<String>(json, r'task'),
|
||||
error: json.containsKey(r'error') ? Optional.present(mapValueOfType<String>(json, r'error')) : const Optional.absent(),
|
||||
progress: json.containsKey(r'progress') ? Optional.present(json[r'progress'] == null ? null : int.parse('${json[r'progress']}')) : const Optional.absent(),
|
||||
task: json.containsKey(r'task') ? Optional.present(mapValueOfType<String>(json, r'task')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+5
-5
@@ -66,12 +66,12 @@ class MapMarkerResponseDto {
|
||||
if (this.city != null) {
|
||||
json[r'city'] = this.city;
|
||||
} else {
|
||||
// json[r'city'] = null;
|
||||
json[r'city'] = null;
|
||||
}
|
||||
if (this.country != null) {
|
||||
json[r'country'] = this.country;
|
||||
} else {
|
||||
// json[r'country'] = null;
|
||||
json[r'country'] = null;
|
||||
}
|
||||
json[r'id'] = this.id;
|
||||
json[r'lat'] = this.lat;
|
||||
@@ -79,7 +79,7 @@ class MapMarkerResponseDto {
|
||||
if (this.state != null) {
|
||||
json[r'state'] = this.state;
|
||||
} else {
|
||||
// json[r'state'] = null;
|
||||
json[r'state'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -96,8 +96,8 @@ class MapMarkerResponseDto {
|
||||
city: mapValueOfType<String>(json, r'city'),
|
||||
country: mapValueOfType<String>(json, r'country'),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
lat: (mapValueOfType<num>(json, r'lat')!).toDouble(),
|
||||
lon: (mapValueOfType<num>(json, r'lon')!).toDouble(),
|
||||
lat: mapValueOfType<double>(json, r'lat')!,
|
||||
lon: mapValueOfType<double>(json, r'lon')!,
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,17 +48,17 @@ class MapReverseGeocodeResponseDto {
|
||||
if (this.city != null) {
|
||||
json[r'city'] = this.city;
|
||||
} else {
|
||||
// json[r'city'] = null;
|
||||
json[r'city'] = null;
|
||||
}
|
||||
if (this.country != null) {
|
||||
json[r'country'] = this.country;
|
||||
} else {
|
||||
// json[r'country'] = null;
|
||||
json[r'country'] = null;
|
||||
}
|
||||
if (this.state != null) {
|
||||
json[r'state'] = this.state;
|
||||
} else {
|
||||
// json[r'state'] = null;
|
||||
json[r'state'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
+12
-14
@@ -13,8 +13,8 @@ part of openapi.api;
|
||||
class MemoriesUpdate {
|
||||
/// Returns a new [MemoriesUpdate] instance.
|
||||
MemoriesUpdate({
|
||||
this.duration,
|
||||
this.enabled,
|
||||
this.duration = const Optional.absent(),
|
||||
this.enabled = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Memory duration in seconds
|
||||
@@ -27,7 +27,7 @@ class MemoriesUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? duration;
|
||||
Optional<int?> duration;
|
||||
|
||||
/// Whether memories are enabled
|
||||
///
|
||||
@@ -36,7 +36,7 @@ class MemoriesUpdate {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? enabled;
|
||||
Optional<bool?> enabled;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MemoriesUpdate &&
|
||||
@@ -54,15 +54,13 @@ class MemoriesUpdate {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.duration != null) {
|
||||
json[r'duration'] = this.duration;
|
||||
} else {
|
||||
// json[r'duration'] = null;
|
||||
if (this.duration.isPresent) {
|
||||
final value = this.duration.value;
|
||||
json[r'duration'] = value;
|
||||
}
|
||||
if (this.enabled != null) {
|
||||
json[r'enabled'] = this.enabled;
|
||||
} else {
|
||||
// json[r'enabled'] = null;
|
||||
if (this.enabled.isPresent) {
|
||||
final value = this.enabled.value;
|
||||
json[r'enabled'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -76,8 +74,8 @@ class MemoriesUpdate {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MemoriesUpdate(
|
||||
duration: mapValueOfType<int>(json, r'duration'),
|
||||
enabled: mapValueOfType<bool>(json, r'enabled'),
|
||||
duration: json.containsKey(r'duration') ? Optional.present(json[r'duration'] == null ? null : int.parse('${json[r'duration']}')) : const Optional.absent(),
|
||||
enabled: json.containsKey(r'enabled') ? Optional.present(mapValueOfType<bool>(json, r'enabled')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+39
-40
@@ -13,18 +13,18 @@ part of openapi.api;
|
||||
class MemoryCreateDto {
|
||||
/// Returns a new [MemoryCreateDto] instance.
|
||||
MemoryCreateDto({
|
||||
this.assetIds = const [],
|
||||
this.assetIds = const Optional.present(const []),
|
||||
required this.data,
|
||||
this.hideAt,
|
||||
this.isSaved,
|
||||
this.hideAt = const Optional.absent(),
|
||||
this.isSaved = const Optional.absent(),
|
||||
required this.memoryAt,
|
||||
this.seenAt,
|
||||
this.showAt,
|
||||
this.seenAt = const Optional.absent(),
|
||||
this.showAt = const Optional.absent(),
|
||||
required this.type,
|
||||
});
|
||||
|
||||
/// Asset IDs to associate with memory
|
||||
List<String> assetIds;
|
||||
Optional<List<String>?> assetIds;
|
||||
|
||||
OnThisDayDto data;
|
||||
|
||||
@@ -35,7 +35,7 @@ class MemoryCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? hideAt;
|
||||
Optional<DateTime?> hideAt;
|
||||
|
||||
/// Is memory saved
|
||||
///
|
||||
@@ -44,7 +44,7 @@ class MemoryCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isSaved;
|
||||
Optional<bool?> isSaved;
|
||||
|
||||
/// Memory date
|
||||
DateTime memoryAt;
|
||||
@@ -56,7 +56,7 @@ class MemoryCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? seenAt;
|
||||
Optional<DateTime?> seenAt;
|
||||
|
||||
/// Date when memory should be shown
|
||||
///
|
||||
@@ -65,7 +65,7 @@ class MemoryCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? showAt;
|
||||
Optional<DateTime?> showAt;
|
||||
|
||||
MemoryType type;
|
||||
|
||||
@@ -97,36 +97,35 @@ class MemoryCreateDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'assetIds'] = this.assetIds;
|
||||
json[r'data'] = this.data;
|
||||
if (this.hideAt != null) {
|
||||
json[r'hideAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.hideAt!.millisecondsSinceEpoch
|
||||
: this.hideAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'hideAt'] = null;
|
||||
if (this.assetIds.isPresent) {
|
||||
final value = this.assetIds.value;
|
||||
json[r'assetIds'] = value;
|
||||
}
|
||||
if (this.isSaved != null) {
|
||||
json[r'isSaved'] = this.isSaved;
|
||||
} else {
|
||||
// json[r'isSaved'] = null;
|
||||
json[r'data'] = this.data;
|
||||
if (this.hideAt.isPresent) {
|
||||
final value = this.hideAt.value;
|
||||
json[r'hideAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.isSaved.isPresent) {
|
||||
final value = this.isSaved.value;
|
||||
json[r'isSaved'] = value;
|
||||
}
|
||||
json[r'memoryAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.memoryAt.millisecondsSinceEpoch
|
||||
: this.memoryAt.toUtc().toIso8601String();
|
||||
if (this.seenAt != null) {
|
||||
json[r'seenAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.seenAt!.millisecondsSinceEpoch
|
||||
: this.seenAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'seenAt'] = null;
|
||||
if (this.seenAt.isPresent) {
|
||||
final value = this.seenAt.value;
|
||||
json[r'seenAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.showAt != null) {
|
||||
json[r'showAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.showAt!.millisecondsSinceEpoch
|
||||
: this.showAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'showAt'] = null;
|
||||
if (this.showAt.isPresent) {
|
||||
final value = this.showAt.value;
|
||||
json[r'showAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
json[r'type'] = this.type;
|
||||
return json;
|
||||
@@ -141,15 +140,15 @@ class MemoryCreateDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MemoryCreateDto(
|
||||
assetIds: json[r'assetIds'] is Iterable
|
||||
assetIds: json.containsKey(r'assetIds') ? Optional.present(json[r'assetIds'] is Iterable
|
||||
? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
: const []) : const Optional.absent(),
|
||||
data: OnThisDayDto.fromJson(json[r'data'])!,
|
||||
hideAt: mapDateTime(json, r'hideAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
isSaved: mapValueOfType<bool>(json, r'isSaved'),
|
||||
hideAt: json.containsKey(r'hideAt') ? Optional.present(mapDateTime(json, r'hideAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
isSaved: json.containsKey(r'isSaved') ? Optional.present(mapValueOfType<bool>(json, r'isSaved')) : const Optional.absent(),
|
||||
memoryAt: mapDateTime(json, r'memoryAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
seenAt: mapDateTime(json, r'seenAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
showAt: mapDateTime(json, r'showAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
seenAt: json.containsKey(r'seenAt') ? Optional.present(mapDateTime(json, r'seenAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
showAt: json.containsKey(r'showAt') ? Optional.present(mapDateTime(json, r'showAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
type: MemoryType.fromJson(json[r'type'])!,
|
||||
);
|
||||
}
|
||||
|
||||
+32
-36
@@ -16,14 +16,14 @@ class MemoryResponseDto {
|
||||
this.assets = const [],
|
||||
required this.createdAt,
|
||||
required this.data,
|
||||
this.deletedAt,
|
||||
this.hideAt,
|
||||
this.deletedAt = const Optional.absent(),
|
||||
this.hideAt = const Optional.absent(),
|
||||
required this.id,
|
||||
required this.isSaved,
|
||||
required this.memoryAt,
|
||||
required this.ownerId,
|
||||
this.seenAt,
|
||||
this.showAt,
|
||||
this.seenAt = const Optional.absent(),
|
||||
this.showAt = const Optional.absent(),
|
||||
required this.type,
|
||||
required this.updatedAt,
|
||||
});
|
||||
@@ -42,7 +42,7 @@ class MemoryResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? deletedAt;
|
||||
Optional<DateTime?> deletedAt;
|
||||
|
||||
/// Date when memory should be hidden
|
||||
///
|
||||
@@ -51,7 +51,7 @@ class MemoryResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? hideAt;
|
||||
Optional<DateTime?> hideAt;
|
||||
|
||||
/// Memory ID
|
||||
String id;
|
||||
@@ -72,7 +72,7 @@ class MemoryResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? seenAt;
|
||||
Optional<DateTime?> seenAt;
|
||||
|
||||
/// Date when memory should be shown
|
||||
///
|
||||
@@ -81,7 +81,7 @@ class MemoryResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? showAt;
|
||||
Optional<DateTime?> showAt;
|
||||
|
||||
MemoryType type;
|
||||
|
||||
@@ -131,19 +131,17 @@ class MemoryResponseDto {
|
||||
? this.createdAt.millisecondsSinceEpoch
|
||||
: this.createdAt.toUtc().toIso8601String();
|
||||
json[r'data'] = this.data;
|
||||
if (this.deletedAt != null) {
|
||||
json[r'deletedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.deletedAt!.millisecondsSinceEpoch
|
||||
: this.deletedAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'deletedAt'] = null;
|
||||
if (this.deletedAt.isPresent) {
|
||||
final value = this.deletedAt.value;
|
||||
json[r'deletedAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.hideAt != null) {
|
||||
json[r'hideAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.hideAt!.millisecondsSinceEpoch
|
||||
: this.hideAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'hideAt'] = null;
|
||||
if (this.hideAt.isPresent) {
|
||||
final value = this.hideAt.value;
|
||||
json[r'hideAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
json[r'id'] = this.id;
|
||||
json[r'isSaved'] = this.isSaved;
|
||||
@@ -151,19 +149,17 @@ class MemoryResponseDto {
|
||||
? this.memoryAt.millisecondsSinceEpoch
|
||||
: this.memoryAt.toUtc().toIso8601String();
|
||||
json[r'ownerId'] = this.ownerId;
|
||||
if (this.seenAt != null) {
|
||||
json[r'seenAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.seenAt!.millisecondsSinceEpoch
|
||||
: this.seenAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'seenAt'] = null;
|
||||
if (this.seenAt.isPresent) {
|
||||
final value = this.seenAt.value;
|
||||
json[r'seenAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.showAt != null) {
|
||||
json[r'showAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.showAt!.millisecondsSinceEpoch
|
||||
: this.showAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'showAt'] = null;
|
||||
if (this.showAt.isPresent) {
|
||||
final value = this.showAt.value;
|
||||
json[r'showAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
json[r'type'] = this.type;
|
||||
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
@@ -184,14 +180,14 @@ class MemoryResponseDto {
|
||||
assets: AssetResponseDto.listFromJson(json[r'assets']),
|
||||
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
data: OnThisDayDto.fromJson(json[r'data'])!,
|
||||
deletedAt: mapDateTime(json, r'deletedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
hideAt: mapDateTime(json, r'hideAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
deletedAt: json.containsKey(r'deletedAt') ? Optional.present(mapDateTime(json, r'deletedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
hideAt: json.containsKey(r'hideAt') ? Optional.present(mapDateTime(json, r'hideAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isSaved: mapValueOfType<bool>(json, r'isSaved')!,
|
||||
memoryAt: mapDateTime(json, r'memoryAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||
seenAt: mapDateTime(json, r'seenAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
showAt: mapDateTime(json, r'showAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
seenAt: json.containsKey(r'seenAt') ? Optional.present(mapDateTime(json, r'seenAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
showAt: json.containsKey(r'showAt') ? Optional.present(mapDateTime(json, r'showAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
type: MemoryType.fromJson(json[r'type'])!,
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
);
|
||||
|
||||
+22
-25
@@ -13,9 +13,9 @@ part of openapi.api;
|
||||
class MemoryUpdateDto {
|
||||
/// Returns a new [MemoryUpdateDto] instance.
|
||||
MemoryUpdateDto({
|
||||
this.isSaved,
|
||||
this.memoryAt,
|
||||
this.seenAt,
|
||||
this.isSaved = const Optional.absent(),
|
||||
this.memoryAt = const Optional.absent(),
|
||||
this.seenAt = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Is memory saved
|
||||
@@ -25,7 +25,7 @@ class MemoryUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isSaved;
|
||||
Optional<bool?> isSaved;
|
||||
|
||||
/// Memory date
|
||||
///
|
||||
@@ -34,7 +34,7 @@ class MemoryUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? memoryAt;
|
||||
Optional<DateTime?> memoryAt;
|
||||
|
||||
/// Date when memory was seen
|
||||
///
|
||||
@@ -43,7 +43,7 @@ class MemoryUpdateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? seenAt;
|
||||
Optional<DateTime?> seenAt;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MemoryUpdateDto &&
|
||||
@@ -63,24 +63,21 @@ class MemoryUpdateDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.isSaved != null) {
|
||||
json[r'isSaved'] = this.isSaved;
|
||||
} else {
|
||||
// json[r'isSaved'] = null;
|
||||
if (this.isSaved.isPresent) {
|
||||
final value = this.isSaved.value;
|
||||
json[r'isSaved'] = value;
|
||||
}
|
||||
if (this.memoryAt != null) {
|
||||
json[r'memoryAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.memoryAt!.millisecondsSinceEpoch
|
||||
: this.memoryAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'memoryAt'] = null;
|
||||
if (this.memoryAt.isPresent) {
|
||||
final value = this.memoryAt.value;
|
||||
json[r'memoryAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.seenAt != null) {
|
||||
json[r'seenAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.seenAt!.millisecondsSinceEpoch
|
||||
: this.seenAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'seenAt'] = null;
|
||||
if (this.seenAt.isPresent) {
|
||||
final value = this.seenAt.value;
|
||||
json[r'seenAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -94,9 +91,9 @@ class MemoryUpdateDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MemoryUpdateDto(
|
||||
isSaved: mapValueOfType<bool>(json, r'isSaved'),
|
||||
memoryAt: mapDateTime(json, r'memoryAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
seenAt: mapDateTime(json, r'seenAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
isSaved: json.containsKey(r'isSaved') ? Optional.present(mapValueOfType<bool>(json, r'isSaved')) : const Optional.absent(),
|
||||
memoryAt: json.containsKey(r'memoryAt') ? Optional.present(mapDateTime(json, r'memoryAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
seenAt: json.containsKey(r'seenAt') ? Optional.present(mapDateTime(json, r'seenAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+273
-307
@@ -13,52 +13,52 @@ part of openapi.api;
|
||||
class MetadataSearchDto {
|
||||
/// Returns a new [MetadataSearchDto] instance.
|
||||
MetadataSearchDto({
|
||||
this.albumIds = const [],
|
||||
this.checksum,
|
||||
this.city,
|
||||
this.country,
|
||||
this.createdAfter,
|
||||
this.createdBefore,
|
||||
this.description,
|
||||
this.encodedVideoPath,
|
||||
this.id,
|
||||
this.isEncoded,
|
||||
this.isFavorite,
|
||||
this.isMotion,
|
||||
this.isNotInAlbum,
|
||||
this.isOffline,
|
||||
this.lensModel,
|
||||
this.libraryId,
|
||||
this.make,
|
||||
this.model,
|
||||
this.ocr,
|
||||
this.order,
|
||||
this.originalFileName,
|
||||
this.originalPath,
|
||||
this.page,
|
||||
this.personIds = const [],
|
||||
this.previewPath,
|
||||
this.rating,
|
||||
this.size,
|
||||
this.state,
|
||||
this.tagIds = const [],
|
||||
this.takenAfter,
|
||||
this.takenBefore,
|
||||
this.thumbnailPath,
|
||||
this.trashedAfter,
|
||||
this.trashedBefore,
|
||||
this.type,
|
||||
this.updatedAfter,
|
||||
this.updatedBefore,
|
||||
this.visibility,
|
||||
this.withDeleted,
|
||||
this.withExif,
|
||||
this.withPeople,
|
||||
this.withStacked,
|
||||
this.albumIds = const Optional.present(const []),
|
||||
this.checksum = const Optional.absent(),
|
||||
this.city = const Optional.absent(),
|
||||
this.country = const Optional.absent(),
|
||||
this.createdAfter = const Optional.absent(),
|
||||
this.createdBefore = const Optional.absent(),
|
||||
this.description = const Optional.absent(),
|
||||
this.encodedVideoPath = const Optional.absent(),
|
||||
this.id = const Optional.absent(),
|
||||
this.isEncoded = const Optional.absent(),
|
||||
this.isFavorite = const Optional.absent(),
|
||||
this.isMotion = const Optional.absent(),
|
||||
this.isNotInAlbum = const Optional.absent(),
|
||||
this.isOffline = const Optional.absent(),
|
||||
this.lensModel = const Optional.absent(),
|
||||
this.libraryId = const Optional.absent(),
|
||||
this.make = const Optional.absent(),
|
||||
this.model = const Optional.absent(),
|
||||
this.ocr = const Optional.absent(),
|
||||
this.order = const Optional.absent(),
|
||||
this.originalFileName = const Optional.absent(),
|
||||
this.originalPath = const Optional.absent(),
|
||||
this.page = const Optional.absent(),
|
||||
this.personIds = const Optional.present(const []),
|
||||
this.previewPath = const Optional.absent(),
|
||||
this.rating = const Optional.absent(),
|
||||
this.size = const Optional.absent(),
|
||||
this.state = const Optional.absent(),
|
||||
this.tagIds = const Optional.present(const []),
|
||||
this.takenAfter = const Optional.absent(),
|
||||
this.takenBefore = const Optional.absent(),
|
||||
this.thumbnailPath = const Optional.absent(),
|
||||
this.trashedAfter = const Optional.absent(),
|
||||
this.trashedBefore = const Optional.absent(),
|
||||
this.type = const Optional.absent(),
|
||||
this.updatedAfter = const Optional.absent(),
|
||||
this.updatedBefore = const Optional.absent(),
|
||||
this.visibility = const Optional.absent(),
|
||||
this.withDeleted = const Optional.absent(),
|
||||
this.withExif = const Optional.absent(),
|
||||
this.withPeople = const Optional.absent(),
|
||||
this.withStacked = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Filter by album IDs
|
||||
List<String> albumIds;
|
||||
Optional<List<String>?> albumIds;
|
||||
|
||||
/// Filter by file checksum
|
||||
///
|
||||
@@ -67,13 +67,13 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? checksum;
|
||||
Optional<String?> checksum;
|
||||
|
||||
/// Filter by city name
|
||||
String? city;
|
||||
Optional<String?> city;
|
||||
|
||||
/// Filter by country name
|
||||
String? country;
|
||||
Optional<String?> country;
|
||||
|
||||
/// Filter by creation date (after)
|
||||
///
|
||||
@@ -82,7 +82,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? createdAfter;
|
||||
Optional<DateTime?> createdAfter;
|
||||
|
||||
/// Filter by creation date (before)
|
||||
///
|
||||
@@ -91,7 +91,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? createdBefore;
|
||||
Optional<DateTime?> createdBefore;
|
||||
|
||||
/// Filter by description text
|
||||
///
|
||||
@@ -100,7 +100,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? description;
|
||||
Optional<String?> description;
|
||||
|
||||
/// Filter by encoded video file path
|
||||
///
|
||||
@@ -109,7 +109,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? encodedVideoPath;
|
||||
Optional<String?> encodedVideoPath;
|
||||
|
||||
/// Filter by asset ID
|
||||
///
|
||||
@@ -118,7 +118,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? id;
|
||||
Optional<String?> id;
|
||||
|
||||
/// Filter by encoded status
|
||||
///
|
||||
@@ -127,7 +127,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isEncoded;
|
||||
Optional<bool?> isEncoded;
|
||||
|
||||
/// Filter by favorite status
|
||||
///
|
||||
@@ -136,7 +136,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isFavorite;
|
||||
Optional<bool?> isFavorite;
|
||||
|
||||
/// Filter by motion photo status
|
||||
///
|
||||
@@ -145,7 +145,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isMotion;
|
||||
Optional<bool?> isMotion;
|
||||
|
||||
/// Filter assets not in any album
|
||||
///
|
||||
@@ -154,7 +154,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isNotInAlbum;
|
||||
Optional<bool?> isNotInAlbum;
|
||||
|
||||
/// Filter by offline status
|
||||
///
|
||||
@@ -163,19 +163,19 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? isOffline;
|
||||
Optional<bool?> isOffline;
|
||||
|
||||
/// Filter by lens model
|
||||
String? lensModel;
|
||||
Optional<String?> lensModel;
|
||||
|
||||
/// Library ID to filter by
|
||||
String? libraryId;
|
||||
Optional<String?> libraryId;
|
||||
|
||||
/// Filter by camera make
|
||||
String? make;
|
||||
Optional<String?> make;
|
||||
|
||||
/// Filter by camera model
|
||||
String? model;
|
||||
Optional<String?> model;
|
||||
|
||||
/// Filter by OCR text content
|
||||
///
|
||||
@@ -184,7 +184,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? ocr;
|
||||
Optional<String?> ocr;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -192,7 +192,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetOrder? order;
|
||||
Optional<AssetOrder?> order;
|
||||
|
||||
/// Filter by original file name
|
||||
///
|
||||
@@ -201,7 +201,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? originalFileName;
|
||||
Optional<String?> originalFileName;
|
||||
|
||||
/// Filter by original file path
|
||||
///
|
||||
@@ -210,7 +210,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? originalPath;
|
||||
Optional<String?> originalPath;
|
||||
|
||||
/// Page number
|
||||
///
|
||||
@@ -222,10 +222,10 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? page;
|
||||
Optional<int?> page;
|
||||
|
||||
/// Filter by person IDs
|
||||
List<String> personIds;
|
||||
Optional<List<String>?> personIds;
|
||||
|
||||
/// Filter by preview file path
|
||||
///
|
||||
@@ -234,13 +234,13 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? previewPath;
|
||||
Optional<String?> previewPath;
|
||||
|
||||
/// Filter by rating [1-5], or null for unrated
|
||||
///
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
int? rating;
|
||||
Optional<int?> rating;
|
||||
|
||||
/// Number of results to return
|
||||
///
|
||||
@@ -252,13 +252,13 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? size;
|
||||
Optional<int?> size;
|
||||
|
||||
/// Filter by state/province name
|
||||
String? state;
|
||||
Optional<String?> state;
|
||||
|
||||
/// Filter by tag IDs
|
||||
List<String>? tagIds;
|
||||
Optional<List<String>?> tagIds;
|
||||
|
||||
/// Filter by taken date (after)
|
||||
///
|
||||
@@ -267,7 +267,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? takenAfter;
|
||||
Optional<DateTime?> takenAfter;
|
||||
|
||||
/// Filter by taken date (before)
|
||||
///
|
||||
@@ -276,7 +276,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? takenBefore;
|
||||
Optional<DateTime?> takenBefore;
|
||||
|
||||
/// Filter by thumbnail file path
|
||||
///
|
||||
@@ -285,7 +285,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? thumbnailPath;
|
||||
Optional<String?> thumbnailPath;
|
||||
|
||||
/// Filter by trash date (after)
|
||||
///
|
||||
@@ -294,7 +294,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? trashedAfter;
|
||||
Optional<DateTime?> trashedAfter;
|
||||
|
||||
/// Filter by trash date (before)
|
||||
///
|
||||
@@ -303,7 +303,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? trashedBefore;
|
||||
Optional<DateTime?> trashedBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -311,7 +311,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetTypeEnum? type;
|
||||
Optional<AssetTypeEnum?> type;
|
||||
|
||||
/// Filter by update date (after)
|
||||
///
|
||||
@@ -320,7 +320,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? updatedAfter;
|
||||
Optional<DateTime?> updatedAfter;
|
||||
|
||||
/// Filter by update date (before)
|
||||
///
|
||||
@@ -329,7 +329,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? updatedBefore;
|
||||
Optional<DateTime?> updatedBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -337,7 +337,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
AssetVisibility? visibility;
|
||||
Optional<AssetVisibility?> visibility;
|
||||
|
||||
/// Include deleted assets
|
||||
///
|
||||
@@ -346,7 +346,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? withDeleted;
|
||||
Optional<bool?> withDeleted;
|
||||
|
||||
/// Include EXIF data in response
|
||||
///
|
||||
@@ -355,7 +355,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? withExif;
|
||||
Optional<bool?> withExif;
|
||||
|
||||
/// Include people data in response
|
||||
///
|
||||
@@ -364,7 +364,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? withPeople;
|
||||
Optional<bool?> withPeople;
|
||||
|
||||
/// Include stacked assets
|
||||
///
|
||||
@@ -373,7 +373,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? withStacked;
|
||||
Optional<bool?> withStacked;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MetadataSearchDto &&
|
||||
@@ -471,223 +471,189 @@ class MetadataSearchDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'albumIds'] = this.albumIds;
|
||||
if (this.checksum != null) {
|
||||
json[r'checksum'] = this.checksum;
|
||||
} else {
|
||||
// json[r'checksum'] = null;
|
||||
if (this.albumIds.isPresent) {
|
||||
final value = this.albumIds.value;
|
||||
json[r'albumIds'] = value;
|
||||
}
|
||||
if (this.city != null) {
|
||||
json[r'city'] = this.city;
|
||||
} else {
|
||||
// json[r'city'] = null;
|
||||
if (this.checksum.isPresent) {
|
||||
final value = this.checksum.value;
|
||||
json[r'checksum'] = value;
|
||||
}
|
||||
if (this.country != null) {
|
||||
json[r'country'] = this.country;
|
||||
} else {
|
||||
// json[r'country'] = null;
|
||||
if (this.city.isPresent) {
|
||||
final value = this.city.value;
|
||||
json[r'city'] = value;
|
||||
}
|
||||
if (this.createdAfter != null) {
|
||||
json[r'createdAfter'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.createdAfter!.millisecondsSinceEpoch
|
||||
: this.createdAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'createdAfter'] = null;
|
||||
if (this.country.isPresent) {
|
||||
final value = this.country.value;
|
||||
json[r'country'] = value;
|
||||
}
|
||||
if (this.createdBefore != null) {
|
||||
json[r'createdBefore'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.createdBefore!.millisecondsSinceEpoch
|
||||
: this.createdBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'createdBefore'] = null;
|
||||
if (this.createdAfter.isPresent) {
|
||||
final value = this.createdAfter.value;
|
||||
json[r'createdAfter'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
} else {
|
||||
// json[r'description'] = null;
|
||||
if (this.createdBefore.isPresent) {
|
||||
final value = this.createdBefore.value;
|
||||
json[r'createdBefore'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.encodedVideoPath != null) {
|
||||
json[r'encodedVideoPath'] = this.encodedVideoPath;
|
||||
} else {
|
||||
// json[r'encodedVideoPath'] = null;
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
if (this.id != null) {
|
||||
json[r'id'] = this.id;
|
||||
} else {
|
||||
// json[r'id'] = null;
|
||||
if (this.encodedVideoPath.isPresent) {
|
||||
final value = this.encodedVideoPath.value;
|
||||
json[r'encodedVideoPath'] = value;
|
||||
}
|
||||
if (this.isEncoded != null) {
|
||||
json[r'isEncoded'] = this.isEncoded;
|
||||
} else {
|
||||
// json[r'isEncoded'] = null;
|
||||
if (this.id.isPresent) {
|
||||
final value = this.id.value;
|
||||
json[r'id'] = value;
|
||||
}
|
||||
if (this.isFavorite != null) {
|
||||
json[r'isFavorite'] = this.isFavorite;
|
||||
} else {
|
||||
// json[r'isFavorite'] = null;
|
||||
if (this.isEncoded.isPresent) {
|
||||
final value = this.isEncoded.value;
|
||||
json[r'isEncoded'] = value;
|
||||
}
|
||||
if (this.isMotion != null) {
|
||||
json[r'isMotion'] = this.isMotion;
|
||||
} else {
|
||||
// json[r'isMotion'] = null;
|
||||
if (this.isFavorite.isPresent) {
|
||||
final value = this.isFavorite.value;
|
||||
json[r'isFavorite'] = value;
|
||||
}
|
||||
if (this.isNotInAlbum != null) {
|
||||
json[r'isNotInAlbum'] = this.isNotInAlbum;
|
||||
} else {
|
||||
// json[r'isNotInAlbum'] = null;
|
||||
if (this.isMotion.isPresent) {
|
||||
final value = this.isMotion.value;
|
||||
json[r'isMotion'] = value;
|
||||
}
|
||||
if (this.isOffline != null) {
|
||||
json[r'isOffline'] = this.isOffline;
|
||||
} else {
|
||||
// json[r'isOffline'] = null;
|
||||
if (this.isNotInAlbum.isPresent) {
|
||||
final value = this.isNotInAlbum.value;
|
||||
json[r'isNotInAlbum'] = value;
|
||||
}
|
||||
if (this.lensModel != null) {
|
||||
json[r'lensModel'] = this.lensModel;
|
||||
} else {
|
||||
// json[r'lensModel'] = null;
|
||||
if (this.isOffline.isPresent) {
|
||||
final value = this.isOffline.value;
|
||||
json[r'isOffline'] = value;
|
||||
}
|
||||
if (this.libraryId != null) {
|
||||
json[r'libraryId'] = this.libraryId;
|
||||
} else {
|
||||
// json[r'libraryId'] = null;
|
||||
if (this.lensModel.isPresent) {
|
||||
final value = this.lensModel.value;
|
||||
json[r'lensModel'] = value;
|
||||
}
|
||||
if (this.make != null) {
|
||||
json[r'make'] = this.make;
|
||||
} else {
|
||||
// json[r'make'] = null;
|
||||
if (this.libraryId.isPresent) {
|
||||
final value = this.libraryId.value;
|
||||
json[r'libraryId'] = value;
|
||||
}
|
||||
if (this.model != null) {
|
||||
json[r'model'] = this.model;
|
||||
} else {
|
||||
// json[r'model'] = null;
|
||||
if (this.make.isPresent) {
|
||||
final value = this.make.value;
|
||||
json[r'make'] = value;
|
||||
}
|
||||
if (this.ocr != null) {
|
||||
json[r'ocr'] = this.ocr;
|
||||
} else {
|
||||
// json[r'ocr'] = null;
|
||||
if (this.model.isPresent) {
|
||||
final value = this.model.value;
|
||||
json[r'model'] = value;
|
||||
}
|
||||
if (this.order != null) {
|
||||
json[r'order'] = this.order;
|
||||
} else {
|
||||
// json[r'order'] = null;
|
||||
if (this.ocr.isPresent) {
|
||||
final value = this.ocr.value;
|
||||
json[r'ocr'] = value;
|
||||
}
|
||||
if (this.originalFileName != null) {
|
||||
json[r'originalFileName'] = this.originalFileName;
|
||||
} else {
|
||||
// json[r'originalFileName'] = null;
|
||||
if (this.order.isPresent) {
|
||||
final value = this.order.value;
|
||||
json[r'order'] = value;
|
||||
}
|
||||
if (this.originalPath != null) {
|
||||
json[r'originalPath'] = this.originalPath;
|
||||
} else {
|
||||
// json[r'originalPath'] = null;
|
||||
if (this.originalFileName.isPresent) {
|
||||
final value = this.originalFileName.value;
|
||||
json[r'originalFileName'] = value;
|
||||
}
|
||||
if (this.page != null) {
|
||||
json[r'page'] = this.page;
|
||||
} else {
|
||||
// json[r'page'] = null;
|
||||
if (this.originalPath.isPresent) {
|
||||
final value = this.originalPath.value;
|
||||
json[r'originalPath'] = value;
|
||||
}
|
||||
json[r'personIds'] = this.personIds;
|
||||
if (this.previewPath != null) {
|
||||
json[r'previewPath'] = this.previewPath;
|
||||
} else {
|
||||
// json[r'previewPath'] = null;
|
||||
if (this.page.isPresent) {
|
||||
final value = this.page.value;
|
||||
json[r'page'] = value;
|
||||
}
|
||||
if (this.rating != null) {
|
||||
json[r'rating'] = this.rating;
|
||||
} else {
|
||||
// json[r'rating'] = null;
|
||||
if (this.personIds.isPresent) {
|
||||
final value = this.personIds.value;
|
||||
json[r'personIds'] = value;
|
||||
}
|
||||
if (this.size != null) {
|
||||
json[r'size'] = this.size;
|
||||
} else {
|
||||
// json[r'size'] = null;
|
||||
if (this.previewPath.isPresent) {
|
||||
final value = this.previewPath.value;
|
||||
json[r'previewPath'] = value;
|
||||
}
|
||||
if (this.state != null) {
|
||||
json[r'state'] = this.state;
|
||||
} else {
|
||||
// json[r'state'] = null;
|
||||
if (this.rating.isPresent) {
|
||||
final value = this.rating.value;
|
||||
json[r'rating'] = value;
|
||||
}
|
||||
if (this.tagIds != null) {
|
||||
json[r'tagIds'] = this.tagIds;
|
||||
} else {
|
||||
// json[r'tagIds'] = null;
|
||||
if (this.size.isPresent) {
|
||||
final value = this.size.value;
|
||||
json[r'size'] = value;
|
||||
}
|
||||
if (this.takenAfter != null) {
|
||||
json[r'takenAfter'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.takenAfter!.millisecondsSinceEpoch
|
||||
: this.takenAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'takenAfter'] = null;
|
||||
if (this.state.isPresent) {
|
||||
final value = this.state.value;
|
||||
json[r'state'] = value;
|
||||
}
|
||||
if (this.takenBefore != null) {
|
||||
json[r'takenBefore'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.takenBefore!.millisecondsSinceEpoch
|
||||
: this.takenBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'takenBefore'] = null;
|
||||
if (this.tagIds.isPresent) {
|
||||
final value = this.tagIds.value;
|
||||
json[r'tagIds'] = value;
|
||||
}
|
||||
if (this.thumbnailPath != null) {
|
||||
json[r'thumbnailPath'] = this.thumbnailPath;
|
||||
} else {
|
||||
// json[r'thumbnailPath'] = null;
|
||||
if (this.takenAfter.isPresent) {
|
||||
final value = this.takenAfter.value;
|
||||
json[r'takenAfter'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.trashedAfter != null) {
|
||||
json[r'trashedAfter'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.trashedAfter!.millisecondsSinceEpoch
|
||||
: this.trashedAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'trashedAfter'] = null;
|
||||
if (this.takenBefore.isPresent) {
|
||||
final value = this.takenBefore.value;
|
||||
json[r'takenBefore'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.trashedBefore != null) {
|
||||
json[r'trashedBefore'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.trashedBefore!.millisecondsSinceEpoch
|
||||
: this.trashedBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'trashedBefore'] = null;
|
||||
if (this.thumbnailPath.isPresent) {
|
||||
final value = this.thumbnailPath.value;
|
||||
json[r'thumbnailPath'] = value;
|
||||
}
|
||||
if (this.type != null) {
|
||||
json[r'type'] = this.type;
|
||||
} else {
|
||||
// json[r'type'] = null;
|
||||
if (this.trashedAfter.isPresent) {
|
||||
final value = this.trashedAfter.value;
|
||||
json[r'trashedAfter'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.updatedAfter != null) {
|
||||
json[r'updatedAfter'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedAfter!.millisecondsSinceEpoch
|
||||
: this.updatedAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'updatedAfter'] = null;
|
||||
if (this.trashedBefore.isPresent) {
|
||||
final value = this.trashedBefore.value;
|
||||
json[r'trashedBefore'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.updatedBefore != null) {
|
||||
json[r'updatedBefore'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedBefore!.millisecondsSinceEpoch
|
||||
: this.updatedBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'updatedBefore'] = null;
|
||||
if (this.type.isPresent) {
|
||||
final value = this.type.value;
|
||||
json[r'type'] = value;
|
||||
}
|
||||
if (this.visibility != null) {
|
||||
json[r'visibility'] = this.visibility;
|
||||
} else {
|
||||
// json[r'visibility'] = null;
|
||||
if (this.updatedAfter.isPresent) {
|
||||
final value = this.updatedAfter.value;
|
||||
json[r'updatedAfter'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.withDeleted != null) {
|
||||
json[r'withDeleted'] = this.withDeleted;
|
||||
} else {
|
||||
// json[r'withDeleted'] = null;
|
||||
if (this.updatedBefore.isPresent) {
|
||||
final value = this.updatedBefore.value;
|
||||
json[r'updatedBefore'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.withExif != null) {
|
||||
json[r'withExif'] = this.withExif;
|
||||
} else {
|
||||
// json[r'withExif'] = null;
|
||||
if (this.visibility.isPresent) {
|
||||
final value = this.visibility.value;
|
||||
json[r'visibility'] = value;
|
||||
}
|
||||
if (this.withPeople != null) {
|
||||
json[r'withPeople'] = this.withPeople;
|
||||
} else {
|
||||
// json[r'withPeople'] = null;
|
||||
if (this.withDeleted.isPresent) {
|
||||
final value = this.withDeleted.value;
|
||||
json[r'withDeleted'] = value;
|
||||
}
|
||||
if (this.withStacked != null) {
|
||||
json[r'withStacked'] = this.withStacked;
|
||||
} else {
|
||||
// json[r'withStacked'] = null;
|
||||
if (this.withExif.isPresent) {
|
||||
final value = this.withExif.value;
|
||||
json[r'withExif'] = value;
|
||||
}
|
||||
if (this.withPeople.isPresent) {
|
||||
final value = this.withPeople.value;
|
||||
json[r'withPeople'] = value;
|
||||
}
|
||||
if (this.withStacked.isPresent) {
|
||||
final value = this.withStacked.value;
|
||||
json[r'withStacked'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -701,54 +667,54 @@ class MetadataSearchDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MetadataSearchDto(
|
||||
albumIds: json[r'albumIds'] is Iterable
|
||||
albumIds: json.containsKey(r'albumIds') ? Optional.present(json[r'albumIds'] is Iterable
|
||||
? (json[r'albumIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
checksum: mapValueOfType<String>(json, r'checksum'),
|
||||
city: mapValueOfType<String>(json, r'city'),
|
||||
country: mapValueOfType<String>(json, r'country'),
|
||||
createdAfter: mapDateTime(json, r'createdAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
createdBefore: mapDateTime(json, r'createdBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
encodedVideoPath: mapValueOfType<String>(json, r'encodedVideoPath'),
|
||||
id: mapValueOfType<String>(json, r'id'),
|
||||
isEncoded: mapValueOfType<bool>(json, r'isEncoded'),
|
||||
isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
|
||||
isMotion: mapValueOfType<bool>(json, r'isMotion'),
|
||||
isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'),
|
||||
isOffline: mapValueOfType<bool>(json, r'isOffline'),
|
||||
lensModel: mapValueOfType<String>(json, r'lensModel'),
|
||||
libraryId: mapValueOfType<String>(json, r'libraryId'),
|
||||
make: mapValueOfType<String>(json, r'make'),
|
||||
model: mapValueOfType<String>(json, r'model'),
|
||||
ocr: mapValueOfType<String>(json, r'ocr'),
|
||||
order: AssetOrder.fromJson(json[r'order']),
|
||||
originalFileName: mapValueOfType<String>(json, r'originalFileName'),
|
||||
originalPath: mapValueOfType<String>(json, r'originalPath'),
|
||||
page: mapValueOfType<int>(json, r'page'),
|
||||
personIds: json[r'personIds'] is Iterable
|
||||
: const []) : const Optional.absent(),
|
||||
checksum: json.containsKey(r'checksum') ? Optional.present(mapValueOfType<String>(json, r'checksum')) : const Optional.absent(),
|
||||
city: json.containsKey(r'city') ? Optional.present(mapValueOfType<String>(json, r'city')) : const Optional.absent(),
|
||||
country: json.containsKey(r'country') ? Optional.present(mapValueOfType<String>(json, r'country')) : const Optional.absent(),
|
||||
createdAfter: json.containsKey(r'createdAfter') ? Optional.present(mapDateTime(json, r'createdAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
createdBefore: json.containsKey(r'createdBefore') ? Optional.present(mapDateTime(json, r'createdBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(mapValueOfType<String>(json, r'description')) : const Optional.absent(),
|
||||
encodedVideoPath: json.containsKey(r'encodedVideoPath') ? Optional.present(mapValueOfType<String>(json, r'encodedVideoPath')) : const Optional.absent(),
|
||||
id: json.containsKey(r'id') ? Optional.present(mapValueOfType<String>(json, r'id')) : const Optional.absent(),
|
||||
isEncoded: json.containsKey(r'isEncoded') ? Optional.present(mapValueOfType<bool>(json, r'isEncoded')) : const Optional.absent(),
|
||||
isFavorite: json.containsKey(r'isFavorite') ? Optional.present(mapValueOfType<bool>(json, r'isFavorite')) : const Optional.absent(),
|
||||
isMotion: json.containsKey(r'isMotion') ? Optional.present(mapValueOfType<bool>(json, r'isMotion')) : const Optional.absent(),
|
||||
isNotInAlbum: json.containsKey(r'isNotInAlbum') ? Optional.present(mapValueOfType<bool>(json, r'isNotInAlbum')) : const Optional.absent(),
|
||||
isOffline: json.containsKey(r'isOffline') ? Optional.present(mapValueOfType<bool>(json, r'isOffline')) : const Optional.absent(),
|
||||
lensModel: json.containsKey(r'lensModel') ? Optional.present(mapValueOfType<String>(json, r'lensModel')) : const Optional.absent(),
|
||||
libraryId: json.containsKey(r'libraryId') ? Optional.present(mapValueOfType<String>(json, r'libraryId')) : const Optional.absent(),
|
||||
make: json.containsKey(r'make') ? Optional.present(mapValueOfType<String>(json, r'make')) : const Optional.absent(),
|
||||
model: json.containsKey(r'model') ? Optional.present(mapValueOfType<String>(json, r'model')) : const Optional.absent(),
|
||||
ocr: json.containsKey(r'ocr') ? Optional.present(mapValueOfType<String>(json, r'ocr')) : const Optional.absent(),
|
||||
order: json.containsKey(r'order') ? Optional.present(AssetOrder.fromJson(json[r'order'])) : const Optional.absent(),
|
||||
originalFileName: json.containsKey(r'originalFileName') ? Optional.present(mapValueOfType<String>(json, r'originalFileName')) : const Optional.absent(),
|
||||
originalPath: json.containsKey(r'originalPath') ? Optional.present(mapValueOfType<String>(json, r'originalPath')) : const Optional.absent(),
|
||||
page: json.containsKey(r'page') ? Optional.present(json[r'page'] == null ? null : int.parse('${json[r'page']}')) : const Optional.absent(),
|
||||
personIds: json.containsKey(r'personIds') ? Optional.present(json[r'personIds'] is Iterable
|
||||
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
previewPath: mapValueOfType<String>(json, r'previewPath'),
|
||||
rating: mapValueOfType<int>(json, r'rating'),
|
||||
size: mapValueOfType<int>(json, r'size'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
tagIds: json[r'tagIds'] is Iterable
|
||||
: const []) : const Optional.absent(),
|
||||
previewPath: json.containsKey(r'previewPath') ? Optional.present(mapValueOfType<String>(json, r'previewPath')) : const Optional.absent(),
|
||||
rating: json.containsKey(r'rating') ? Optional.present(json[r'rating'] == null ? null : int.parse('${json[r'rating']}')) : const Optional.absent(),
|
||||
size: json.containsKey(r'size') ? Optional.present(json[r'size'] == null ? null : int.parse('${json[r'size']}')) : const Optional.absent(),
|
||||
state: json.containsKey(r'state') ? Optional.present(mapValueOfType<String>(json, r'state')) : const Optional.absent(),
|
||||
tagIds: json.containsKey(r'tagIds') ? Optional.present(json[r'tagIds'] is Iterable
|
||||
? (json[r'tagIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
takenAfter: mapDateTime(json, r'takenAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
takenBefore: mapDateTime(json, r'takenBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath'),
|
||||
trashedAfter: mapDateTime(json, r'trashedAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
trashedBefore: mapDateTime(json, r'trashedBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
type: AssetTypeEnum.fromJson(json[r'type']),
|
||||
updatedAfter: mapDateTime(json, r'updatedAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
updatedBefore: mapDateTime(json, r'updatedBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
visibility: AssetVisibility.fromJson(json[r'visibility']),
|
||||
withDeleted: mapValueOfType<bool>(json, r'withDeleted'),
|
||||
withExif: mapValueOfType<bool>(json, r'withExif'),
|
||||
withPeople: mapValueOfType<bool>(json, r'withPeople'),
|
||||
withStacked: mapValueOfType<bool>(json, r'withStacked'),
|
||||
: const []) : const Optional.absent(),
|
||||
takenAfter: json.containsKey(r'takenAfter') ? Optional.present(mapDateTime(json, r'takenAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
takenBefore: json.containsKey(r'takenBefore') ? Optional.present(mapDateTime(json, r'takenBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
thumbnailPath: json.containsKey(r'thumbnailPath') ? Optional.present(mapValueOfType<String>(json, r'thumbnailPath')) : const Optional.absent(),
|
||||
trashedAfter: json.containsKey(r'trashedAfter') ? Optional.present(mapDateTime(json, r'trashedAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
trashedBefore: json.containsKey(r'trashedBefore') ? Optional.present(mapDateTime(json, r'trashedBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
type: json.containsKey(r'type') ? Optional.present(AssetTypeEnum.fromJson(json[r'type'])) : const Optional.absent(),
|
||||
updatedAfter: json.containsKey(r'updatedAfter') ? Optional.present(mapDateTime(json, r'updatedAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
updatedBefore: json.containsKey(r'updatedBefore') ? Optional.present(mapDateTime(json, r'updatedBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
visibility: json.containsKey(r'visibility') ? Optional.present(AssetVisibility.fromJson(json[r'visibility'])) : const Optional.absent(),
|
||||
withDeleted: json.containsKey(r'withDeleted') ? Optional.present(mapValueOfType<bool>(json, r'withDeleted')) : const Optional.absent(),
|
||||
withExif: json.containsKey(r'withExif') ? Optional.present(mapValueOfType<bool>(json, r'withExif')) : const Optional.absent(),
|
||||
withPeople: json.containsKey(r'withPeople') ? Optional.present(mapValueOfType<bool>(json, r'withPeople')) : const Optional.absent(),
|
||||
withStacked: json.containsKey(r'withStacked') ? Optional.present(mapValueOfType<bool>(json, r'withStacked')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+33
-34
@@ -13,20 +13,20 @@ part of openapi.api;
|
||||
class NotificationCreateDto {
|
||||
/// Returns a new [NotificationCreateDto] instance.
|
||||
NotificationCreateDto({
|
||||
this.data = const {},
|
||||
this.description,
|
||||
this.level,
|
||||
this.readAt,
|
||||
this.data = const Optional.present(const {}),
|
||||
this.description = const Optional.absent(),
|
||||
this.level = const Optional.absent(),
|
||||
this.readAt = const Optional.absent(),
|
||||
required this.title,
|
||||
this.type,
|
||||
this.type = const Optional.absent(),
|
||||
required this.userId,
|
||||
});
|
||||
|
||||
/// Additional notification data
|
||||
Map<String, Object> data;
|
||||
Optional<Map<String, Object>?> data;
|
||||
|
||||
/// Notification description
|
||||
String? description;
|
||||
Optional<String?> description;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -34,10 +34,10 @@ class NotificationCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
NotificationLevel? level;
|
||||
Optional<NotificationLevel?> level;
|
||||
|
||||
/// Date when notification was read
|
||||
DateTime? readAt;
|
||||
Optional<DateTime?> readAt;
|
||||
|
||||
/// Notification title
|
||||
String title;
|
||||
@@ -48,7 +48,7 @@ class NotificationCreateDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
NotificationType? type;
|
||||
Optional<NotificationType?> type;
|
||||
|
||||
/// User ID to send notification to
|
||||
String userId;
|
||||
@@ -79,29 +79,28 @@ class NotificationCreateDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'data'] = this.data;
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
} else {
|
||||
// json[r'description'] = null;
|
||||
if (this.data.isPresent) {
|
||||
final value = this.data.value;
|
||||
json[r'data'] = value;
|
||||
}
|
||||
if (this.level != null) {
|
||||
json[r'level'] = this.level;
|
||||
} else {
|
||||
// json[r'level'] = null;
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
if (this.readAt != null) {
|
||||
json[r'readAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.readAt!.millisecondsSinceEpoch
|
||||
: this.readAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'readAt'] = null;
|
||||
if (this.level.isPresent) {
|
||||
final value = this.level.value;
|
||||
json[r'level'] = value;
|
||||
}
|
||||
if (this.readAt.isPresent) {
|
||||
final value = this.readAt.value;
|
||||
json[r'readAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
json[r'title'] = this.title;
|
||||
if (this.type != null) {
|
||||
json[r'type'] = this.type;
|
||||
} else {
|
||||
// json[r'type'] = null;
|
||||
if (this.type.isPresent) {
|
||||
final value = this.type.value;
|
||||
json[r'type'] = value;
|
||||
}
|
||||
json[r'userId'] = this.userId;
|
||||
return json;
|
||||
@@ -116,12 +115,12 @@ class NotificationCreateDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return NotificationCreateDto(
|
||||
data: mapCastOfType<String, Object>(json, r'data') ?? const {},
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
level: NotificationLevel.fromJson(json[r'level']),
|
||||
readAt: mapDateTime(json, r'readAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
data: json.containsKey(r'data') ? Optional.present(mapCastOfType<String, Object>(json, r'data')) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(mapValueOfType<String>(json, r'description')) : const Optional.absent(),
|
||||
level: json.containsKey(r'level') ? Optional.present(NotificationLevel.fromJson(json[r'level'])) : const Optional.absent(),
|
||||
readAt: json.containsKey(r'readAt') ? Optional.present(mapDateTime(json, r'readAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
title: mapValueOfType<String>(json, r'title')!,
|
||||
type: NotificationType.fromJson(json[r'type']),
|
||||
type: json.containsKey(r'type') ? Optional.present(NotificationType.fromJson(json[r'type'])) : const Optional.absent(),
|
||||
userId: mapValueOfType<String>(json, r'userId')!,
|
||||
);
|
||||
}
|
||||
|
||||
+21
-20
@@ -14,11 +14,11 @@ class NotificationDto {
|
||||
/// Returns a new [NotificationDto] instance.
|
||||
NotificationDto({
|
||||
required this.createdAt,
|
||||
this.data = const {},
|
||||
this.description,
|
||||
this.data = const Optional.present(const {}),
|
||||
this.description = const Optional.absent(),
|
||||
required this.id,
|
||||
required this.level,
|
||||
this.readAt,
|
||||
this.readAt = const Optional.absent(),
|
||||
required this.title,
|
||||
required this.type,
|
||||
});
|
||||
@@ -27,7 +27,7 @@ class NotificationDto {
|
||||
DateTime createdAt;
|
||||
|
||||
/// Additional notification data
|
||||
Map<String, Object> data;
|
||||
Optional<Map<String, Object>?> data;
|
||||
|
||||
/// Notification description
|
||||
///
|
||||
@@ -36,7 +36,7 @@ class NotificationDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? description;
|
||||
Optional<String?> description;
|
||||
|
||||
/// Notification ID
|
||||
String id;
|
||||
@@ -50,7 +50,7 @@ class NotificationDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
DateTime? readAt;
|
||||
Optional<DateTime?> readAt;
|
||||
|
||||
/// Notification title
|
||||
String title;
|
||||
@@ -88,20 +88,21 @@ class NotificationDto {
|
||||
json[r'createdAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.createdAt.millisecondsSinceEpoch
|
||||
: this.createdAt.toUtc().toIso8601String();
|
||||
json[r'data'] = this.data;
|
||||
if (this.description != null) {
|
||||
json[r'description'] = this.description;
|
||||
} else {
|
||||
// json[r'description'] = null;
|
||||
if (this.data.isPresent) {
|
||||
final value = this.data.value;
|
||||
json[r'data'] = value;
|
||||
}
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
json[r'id'] = this.id;
|
||||
json[r'level'] = this.level;
|
||||
if (this.readAt != null) {
|
||||
json[r'readAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.readAt!.millisecondsSinceEpoch
|
||||
: this.readAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'readAt'] = null;
|
||||
if (this.readAt.isPresent) {
|
||||
final value = this.readAt.value;
|
||||
json[r'readAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
json[r'title'] = this.title;
|
||||
json[r'type'] = this.type;
|
||||
@@ -118,11 +119,11 @@ class NotificationDto {
|
||||
|
||||
return NotificationDto(
|
||||
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
data: mapCastOfType<String, Object>(json, r'data') ?? const {},
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
data: json.containsKey(r'data') ? Optional.present(mapCastOfType<String, Object>(json, r'data')) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(mapValueOfType<String>(json, r'description')) : const Optional.absent(),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
level: NotificationLevel.fromJson(json[r'level'])!,
|
||||
readAt: mapDateTime(json, r'readAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
readAt: json.containsKey(r'readAt') ? Optional.present(mapDateTime(json, r'readAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
title: mapValueOfType<String>(json, r'title')!,
|
||||
type: NotificationType.fromJson(json[r'type'])!,
|
||||
);
|
||||
|
||||
+8
-9
@@ -14,14 +14,14 @@ class NotificationUpdateAllDto {
|
||||
/// Returns a new [NotificationUpdateAllDto] instance.
|
||||
NotificationUpdateAllDto({
|
||||
this.ids = const [],
|
||||
this.readAt,
|
||||
this.readAt = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Notification IDs to update
|
||||
List<String> ids;
|
||||
|
||||
/// Date when notifications were read
|
||||
DateTime? readAt;
|
||||
Optional<DateTime?> readAt;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is NotificationUpdateAllDto &&
|
||||
@@ -40,12 +40,11 @@ class NotificationUpdateAllDto {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'ids'] = this.ids;
|
||||
if (this.readAt != null) {
|
||||
json[r'readAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.readAt!.millisecondsSinceEpoch
|
||||
: this.readAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'readAt'] = null;
|
||||
if (this.readAt.isPresent) {
|
||||
final value = this.readAt.value;
|
||||
json[r'readAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -62,7 +61,7 @@ class NotificationUpdateAllDto {
|
||||
ids: json[r'ids'] is Iterable
|
||||
? (json[r'ids'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
readAt: mapDateTime(json, r'readAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
readAt: json.containsKey(r'readAt') ? Optional.present(mapDateTime(json, r'readAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+8
-9
@@ -13,11 +13,11 @@ part of openapi.api;
|
||||
class NotificationUpdateDto {
|
||||
/// Returns a new [NotificationUpdateDto] instance.
|
||||
NotificationUpdateDto({
|
||||
this.readAt,
|
||||
this.readAt = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// Date when notification was read
|
||||
DateTime? readAt;
|
||||
Optional<DateTime?> readAt;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is NotificationUpdateDto &&
|
||||
@@ -33,12 +33,11 @@ class NotificationUpdateDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.readAt != null) {
|
||||
json[r'readAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.readAt!.millisecondsSinceEpoch
|
||||
: this.readAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'readAt'] = null;
|
||||
if (this.readAt.isPresent) {
|
||||
final value = this.readAt.value;
|
||||
json[r'readAt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -52,7 +51,7 @@ class NotificationUpdateDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return NotificationUpdateDto(
|
||||
readAt: mapDateTime(json, r'readAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
readAt: json.containsKey(r'readAt') ? Optional.present(mapDateTime(json, r'readAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
+12
-14
@@ -13,8 +13,8 @@ part of openapi.api;
|
||||
class OAuthCallbackDto {
|
||||
/// Returns a new [OAuthCallbackDto] instance.
|
||||
OAuthCallbackDto({
|
||||
this.codeVerifier,
|
||||
this.state,
|
||||
this.codeVerifier = const Optional.absent(),
|
||||
this.state = const Optional.absent(),
|
||||
required this.url,
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ class OAuthCallbackDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? codeVerifier;
|
||||
Optional<String?> codeVerifier;
|
||||
|
||||
/// OAuth state parameter
|
||||
///
|
||||
@@ -34,7 +34,7 @@ class OAuthCallbackDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? state;
|
||||
Optional<String?> state;
|
||||
|
||||
/// OAuth callback URL
|
||||
String url;
|
||||
@@ -57,15 +57,13 @@ class OAuthCallbackDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.codeVerifier != null) {
|
||||
json[r'codeVerifier'] = this.codeVerifier;
|
||||
} else {
|
||||
// json[r'codeVerifier'] = null;
|
||||
if (this.codeVerifier.isPresent) {
|
||||
final value = this.codeVerifier.value;
|
||||
json[r'codeVerifier'] = value;
|
||||
}
|
||||
if (this.state != null) {
|
||||
json[r'state'] = this.state;
|
||||
} else {
|
||||
// json[r'state'] = null;
|
||||
if (this.state.isPresent) {
|
||||
final value = this.state.value;
|
||||
json[r'state'] = value;
|
||||
}
|
||||
json[r'url'] = this.url;
|
||||
return json;
|
||||
@@ -80,8 +78,8 @@ class OAuthCallbackDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return OAuthCallbackDto(
|
||||
codeVerifier: mapValueOfType<String>(json, r'codeVerifier'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
codeVerifier: json.containsKey(r'codeVerifier') ? Optional.present(mapValueOfType<String>(json, r'codeVerifier')) : const Optional.absent(),
|
||||
state: json.containsKey(r'state') ? Optional.present(mapValueOfType<String>(json, r'state')) : const Optional.absent(),
|
||||
url: mapValueOfType<String>(json, r'url')!,
|
||||
);
|
||||
}
|
||||
|
||||
+12
-14
@@ -13,9 +13,9 @@ part of openapi.api;
|
||||
class OAuthConfigDto {
|
||||
/// Returns a new [OAuthConfigDto] instance.
|
||||
OAuthConfigDto({
|
||||
this.codeChallenge,
|
||||
this.codeChallenge = const Optional.absent(),
|
||||
required this.redirectUri,
|
||||
this.state,
|
||||
this.state = const Optional.absent(),
|
||||
});
|
||||
|
||||
/// OAuth code challenge (PKCE)
|
||||
@@ -25,7 +25,7 @@ class OAuthConfigDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? codeChallenge;
|
||||
Optional<String?> codeChallenge;
|
||||
|
||||
/// OAuth redirect URI
|
||||
String redirectUri;
|
||||
@@ -37,7 +37,7 @@ class OAuthConfigDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? state;
|
||||
Optional<String?> state;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is OAuthConfigDto &&
|
||||
@@ -57,16 +57,14 @@ class OAuthConfigDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.codeChallenge != null) {
|
||||
json[r'codeChallenge'] = this.codeChallenge;
|
||||
} else {
|
||||
// json[r'codeChallenge'] = null;
|
||||
if (this.codeChallenge.isPresent) {
|
||||
final value = this.codeChallenge.value;
|
||||
json[r'codeChallenge'] = value;
|
||||
}
|
||||
json[r'redirectUri'] = this.redirectUri;
|
||||
if (this.state != null) {
|
||||
json[r'state'] = this.state;
|
||||
} else {
|
||||
// json[r'state'] = null;
|
||||
if (this.state.isPresent) {
|
||||
final value = this.state.value;
|
||||
json[r'state'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@@ -80,9 +78,9 @@ class OAuthConfigDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return OAuthConfigDto(
|
||||
codeChallenge: mapValueOfType<String>(json, r'codeChallenge'),
|
||||
codeChallenge: json.containsKey(r'codeChallenge') ? Optional.present(mapValueOfType<String>(json, r'codeChallenge')) : const Optional.absent(),
|
||||
redirectUri: mapValueOfType<String>(json, r'redirectUri')!,
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
state: json.containsKey(r'state') ? Optional.present(mapValueOfType<String>(json, r'state')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -23,13 +23,13 @@ class OAuthTokenEndpointAuthMethod {
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const post = OAuthTokenEndpointAuthMethod._(r'client_secret_post');
|
||||
static const basic = OAuthTokenEndpointAuthMethod._(r'client_secret_basic');
|
||||
static const clientSecretPost = OAuthTokenEndpointAuthMethod._(r'client_secret_post');
|
||||
static const clientSecretBasic = OAuthTokenEndpointAuthMethod._(r'client_secret_basic');
|
||||
|
||||
/// List of all possible values in this [enum][OAuthTokenEndpointAuthMethod].
|
||||
static const values = <OAuthTokenEndpointAuthMethod>[
|
||||
post,
|
||||
basic,
|
||||
clientSecretPost,
|
||||
clientSecretBasic,
|
||||
];
|
||||
|
||||
static OAuthTokenEndpointAuthMethod? fromJson(dynamic value) => OAuthTokenEndpointAuthMethodTypeTransformer().decode(value);
|
||||
@@ -68,8 +68,8 @@ class OAuthTokenEndpointAuthMethodTypeTransformer {
|
||||
OAuthTokenEndpointAuthMethod? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'client_secret_post': return OAuthTokenEndpointAuthMethod.post;
|
||||
case r'client_secret_basic': return OAuthTokenEndpointAuthMethod.basic;
|
||||
case r'client_secret_post': return OAuthTokenEndpointAuthMethod.clientSecretPost;
|
||||
case r'client_secret_basic': return OAuthTokenEndpointAuthMethod.clientSecretBasic;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
|
||||
Generated
+2
-2
@@ -85,8 +85,8 @@ class OcrConfig {
|
||||
return OcrConfig(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
maxResolution: mapValueOfType<int>(json, r'maxResolution')!,
|
||||
minDetectionScore: (mapValueOfType<num>(json, r'minDetectionScore')!).toDouble(),
|
||||
minRecognitionScore: (mapValueOfType<num>(json, r'minRecognitionScore')!).toDouble(),
|
||||
minDetectionScore: mapValueOfType<double>(json, r'minDetectionScore')!,
|
||||
minRecognitionScore: mapValueOfType<double>(json, r'minRecognitionScore')!,
|
||||
modelName: mapValueOfType<String>(json, r'modelName')!,
|
||||
);
|
||||
}
|
||||
|
||||
+6
-6
@@ -23,13 +23,13 @@ class PartnerDirection {
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const by = PartnerDirection._(r'shared-by');
|
||||
static const with_ = PartnerDirection._(r'shared-with');
|
||||
static const sharedBy = PartnerDirection._(r'shared-by');
|
||||
static const sharedWith = PartnerDirection._(r'shared-with');
|
||||
|
||||
/// List of all possible values in this [enum][PartnerDirection].
|
||||
static const values = <PartnerDirection>[
|
||||
by,
|
||||
with_,
|
||||
sharedBy,
|
||||
sharedWith,
|
||||
];
|
||||
|
||||
static PartnerDirection? fromJson(dynamic value) => PartnerDirectionTypeTransformer().decode(value);
|
||||
@@ -68,8 +68,8 @@ class PartnerDirectionTypeTransformer {
|
||||
PartnerDirection? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'shared-by': return PartnerDirection.by;
|
||||
case r'shared-with': return PartnerDirection.with_;
|
||||
case r'shared-by': return PartnerDirection.sharedBy;
|
||||
case r'shared-with': return PartnerDirection.sharedWith;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
|
||||
+6
-7
@@ -16,7 +16,7 @@ class PartnerResponseDto {
|
||||
required this.avatarColor,
|
||||
required this.email,
|
||||
required this.id,
|
||||
this.inTimeline,
|
||||
this.inTimeline = const Optional.absent(),
|
||||
required this.name,
|
||||
required this.profileChangedAt,
|
||||
required this.profileImagePath,
|
||||
@@ -37,7 +37,7 @@ class PartnerResponseDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
bool? inTimeline;
|
||||
Optional<bool?> inTimeline;
|
||||
|
||||
/// User name
|
||||
String name;
|
||||
@@ -77,10 +77,9 @@ class PartnerResponseDto {
|
||||
json[r'avatarColor'] = this.avatarColor;
|
||||
json[r'email'] = this.email;
|
||||
json[r'id'] = this.id;
|
||||
if (this.inTimeline != null) {
|
||||
json[r'inTimeline'] = this.inTimeline;
|
||||
} else {
|
||||
// json[r'inTimeline'] = null;
|
||||
if (this.inTimeline.isPresent) {
|
||||
final value = this.inTimeline.value;
|
||||
json[r'inTimeline'] = value;
|
||||
}
|
||||
json[r'name'] = this.name;
|
||||
json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String();
|
||||
@@ -100,7 +99,7 @@ class PartnerResponseDto {
|
||||
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
|
||||
email: mapValueOfType<String>(json, r'email')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
inTimeline: mapValueOfType<bool>(json, r'inTimeline'),
|
||||
inTimeline: json.containsKey(r'inTimeline') ? Optional.present(mapValueOfType<bool>(json, r'inTimeline')) : const Optional.absent(),
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!,
|
||||
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
|
||||
|
||||
+6
-7
@@ -14,7 +14,7 @@ class PeopleResponse {
|
||||
/// Returns a new [PeopleResponse] instance.
|
||||
PeopleResponse({
|
||||
required this.enabled,
|
||||
this.minimumFaces,
|
||||
this.minimumFaces = const Optional.absent(),
|
||||
required this.sidebarWeb,
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ class PeopleResponse {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? minimumFaces;
|
||||
Optional<int?> minimumFaces;
|
||||
|
||||
/// Whether people appear in web sidebar
|
||||
bool sidebarWeb;
|
||||
@@ -55,10 +55,9 @@ class PeopleResponse {
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'enabled'] = this.enabled;
|
||||
if (this.minimumFaces != null) {
|
||||
json[r'minimumFaces'] = this.minimumFaces;
|
||||
} else {
|
||||
// json[r'minimumFaces'] = null;
|
||||
if (this.minimumFaces.isPresent) {
|
||||
final value = this.minimumFaces.value;
|
||||
json[r'minimumFaces'] = value;
|
||||
}
|
||||
json[r'sidebarWeb'] = this.sidebarWeb;
|
||||
return json;
|
||||
@@ -74,7 +73,7 @@ class PeopleResponse {
|
||||
|
||||
return PeopleResponse(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
minimumFaces: mapValueOfType<int>(json, r'minimumFaces'),
|
||||
minimumFaces: json.containsKey(r'minimumFaces') ? Optional.present(json[r'minimumFaces'] == null ? null : int.parse('${json[r'minimumFaces']}')) : const Optional.absent(),
|
||||
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb')!,
|
||||
);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user